@hyperfixi/patterns-reference 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/src/api/llm.ts ADDED
@@ -0,0 +1,357 @@
1
+ /**
2
+ * LLM Examples API
3
+ *
4
+ * Provides functions to query and manage LLM examples for few-shot learning.
5
+ */
6
+
7
+ import { getDatabase } from '../database/connection';
8
+ import type { LLMExample, ConnectionOptions } from '../types';
9
+
10
+ // =============================================================================
11
+ // Database Row Types
12
+ // =============================================================================
13
+
14
+ interface LLMExampleRow {
15
+ id: number;
16
+ code_example_id: string;
17
+ language: string;
18
+ prompt: string;
19
+ completion: string;
20
+ quality_score: number;
21
+ usage_count: number;
22
+ created_at: string;
23
+ }
24
+
25
+ // =============================================================================
26
+ // Query Functions
27
+ // =============================================================================
28
+
29
+ /**
30
+ * Get LLM examples relevant to a prompt.
31
+ * Uses text similarity to find relevant patterns.
32
+ */
33
+ export async function getLLMExamples(
34
+ prompt: string,
35
+ language: string = 'en',
36
+ limit: number = 5,
37
+ options?: ConnectionOptions
38
+ ): Promise<LLMExample[]> {
39
+ const db = getDatabase({ ...options, readonly: true });
40
+
41
+ // Extract keywords from prompt
42
+ const keywords = extractKeywords(prompt);
43
+
44
+ if (keywords.length === 0) {
45
+ // Return top-quality examples as fallback
46
+ const rows = db
47
+ .prepare(
48
+ `
49
+ SELECT * FROM llm_examples
50
+ WHERE language = ?
51
+ ORDER BY quality_score DESC, usage_count DESC
52
+ LIMIT ?
53
+ `
54
+ )
55
+ .all(language, limit) as LLMExampleRow[];
56
+
57
+ // Track usage
58
+ trackUsage(
59
+ db,
60
+ rows.map(r => r.id)
61
+ );
62
+
63
+ return rows.map(mapRowToLLMExample);
64
+ }
65
+
66
+ // Build LIKE clauses for keyword matching
67
+ const likeClauses = keywords.map(() => '(prompt LIKE ? OR completion LIKE ?)').join(' OR ');
68
+ const params = keywords.flatMap(k => [`%${k}%`, `%${k}%`]);
69
+
70
+ const rows = db
71
+ .prepare(
72
+ `
73
+ SELECT * FROM llm_examples
74
+ WHERE language = ? AND (${likeClauses})
75
+ ORDER BY quality_score DESC
76
+ LIMIT ?
77
+ `
78
+ )
79
+ .all(language, ...params, limit) as LLMExampleRow[];
80
+
81
+ // Track usage
82
+ trackUsage(
83
+ db,
84
+ rows.map(r => r.id)
85
+ );
86
+
87
+ return rows.map(mapRowToLLMExample);
88
+ }
89
+
90
+ /**
91
+ * Get examples by command type.
92
+ */
93
+ export async function getExamplesByCommand(
94
+ command: string,
95
+ language: string = 'en',
96
+ limit: number = 5,
97
+ options?: ConnectionOptions
98
+ ): Promise<LLMExample[]> {
99
+ const db = getDatabase({ ...options, readonly: true });
100
+
101
+ const rows = db
102
+ .prepare(
103
+ `
104
+ SELECT * FROM llm_examples
105
+ WHERE language = ? AND completion LIKE ?
106
+ ORDER BY quality_score DESC
107
+ LIMIT ?
108
+ `
109
+ )
110
+ .all(language, `%${command}%`, limit) as LLMExampleRow[];
111
+
112
+ return rows.map(mapRowToLLMExample);
113
+ }
114
+
115
+ /**
116
+ * Get high-quality examples (for few-shot prompts).
117
+ */
118
+ export async function getHighQualityExamples(
119
+ language: string = 'en',
120
+ minQuality: number = 0.8,
121
+ limit: number = 10,
122
+ options?: ConnectionOptions
123
+ ): Promise<LLMExample[]> {
124
+ const db = getDatabase({ ...options, readonly: true });
125
+
126
+ const rows = db
127
+ .prepare(
128
+ `
129
+ SELECT * FROM llm_examples
130
+ WHERE language = ? AND quality_score >= ?
131
+ ORDER BY quality_score DESC, usage_count DESC
132
+ LIMIT ?
133
+ `
134
+ )
135
+ .all(language, minQuality, limit) as LLMExampleRow[];
136
+
137
+ return rows.map(mapRowToLLMExample);
138
+ }
139
+
140
+ /**
141
+ * Get most used examples (popular).
142
+ */
143
+ export async function getMostUsedExamples(
144
+ language: string = 'en',
145
+ limit: number = 10,
146
+ options?: ConnectionOptions
147
+ ): Promise<LLMExample[]> {
148
+ const db = getDatabase({ ...options, readonly: true });
149
+
150
+ const rows = db
151
+ .prepare(
152
+ `
153
+ SELECT * FROM llm_examples
154
+ WHERE language = ?
155
+ ORDER BY usage_count DESC, quality_score DESC
156
+ LIMIT ?
157
+ `
158
+ )
159
+ .all(language, limit) as LLMExampleRow[];
160
+
161
+ return rows.map(mapRowToLLMExample);
162
+ }
163
+
164
+ /**
165
+ * Build few-shot context for LLM prompting.
166
+ */
167
+ export async function buildFewShotContext(
168
+ prompt: string,
169
+ language: string = 'en',
170
+ numExamples: number = 3,
171
+ options?: ConnectionOptions
172
+ ): Promise<string> {
173
+ const examples = await getLLMExamples(prompt, language, numExamples, options);
174
+
175
+ if (examples.length === 0) {
176
+ return '';
177
+ }
178
+
179
+ let context = 'Here are some example hyperscript patterns:\n\n';
180
+
181
+ for (const ex of examples) {
182
+ context += `Task: ${ex.prompt}\n`;
183
+ context += `Code: ${ex.completion}\n\n`;
184
+ }
185
+
186
+ context += `Now generate hyperscript for: ${prompt}\n`;
187
+
188
+ return context;
189
+ }
190
+
191
+ /**
192
+ * Add a new LLM example.
193
+ */
194
+ export async function addLLMExample(
195
+ example: Omit<LLMExample, 'id' | 'usageCount' | 'createdAt'>,
196
+ options?: ConnectionOptions
197
+ ): Promise<number> {
198
+ const db = getDatabase(options);
199
+
200
+ const result = db
201
+ .prepare(
202
+ `
203
+ INSERT INTO llm_examples
204
+ (code_example_id, language, prompt, completion, quality_score, usage_count, created_at)
205
+ VALUES (?, ?, ?, ?, ?, 0, datetime('now'))
206
+ `
207
+ )
208
+ .run(
209
+ example.patternId,
210
+ example.language,
211
+ example.prompt,
212
+ example.completion,
213
+ example.qualityScore
214
+ );
215
+
216
+ return result.lastInsertRowid as number;
217
+ }
218
+
219
+ /**
220
+ * Update example quality score.
221
+ */
222
+ export async function updateQualityScore(
223
+ id: number,
224
+ qualityScore: number,
225
+ options?: ConnectionOptions
226
+ ): Promise<void> {
227
+ const db = getDatabase(options);
228
+
229
+ db.prepare(
230
+ `
231
+ UPDATE llm_examples SET quality_score = ? WHERE id = ?
232
+ `
233
+ ).run(qualityScore, id);
234
+ }
235
+
236
+ /**
237
+ * Get LLM example statistics.
238
+ */
239
+ export async function getLLMStats(options?: ConnectionOptions): Promise<{
240
+ total: number;
241
+ byLanguage: Record<string, number>;
242
+ avgQuality: number;
243
+ totalUsage: number;
244
+ }> {
245
+ const db = getDatabase({ ...options, readonly: true });
246
+
247
+ const totalResult = db.prepare('SELECT COUNT(*) as count FROM llm_examples').get() as {
248
+ count: number;
249
+ };
250
+
251
+ const byLangResult = db
252
+ .prepare(
253
+ `
254
+ SELECT language, COUNT(*) as count
255
+ FROM llm_examples
256
+ GROUP BY language
257
+ `
258
+ )
259
+ .all() as { language: string; count: number }[];
260
+
261
+ const avgResult = db.prepare('SELECT AVG(quality_score) as avg FROM llm_examples').get() as {
262
+ avg: number;
263
+ };
264
+
265
+ const usageResult = db.prepare('SELECT SUM(usage_count) as total FROM llm_examples').get() as {
266
+ total: number;
267
+ };
268
+
269
+ const byLanguage: Record<string, number> = {};
270
+ for (const { language, count } of byLangResult) {
271
+ byLanguage[language] = count;
272
+ }
273
+
274
+ return {
275
+ total: totalResult.count,
276
+ byLanguage,
277
+ avgQuality: avgResult.avg || 0,
278
+ totalUsage: usageResult.total || 0,
279
+ };
280
+ }
281
+
282
+ // =============================================================================
283
+ // Helper Functions
284
+ // =============================================================================
285
+
286
+ /**
287
+ * Extract keywords from a prompt for matching.
288
+ */
289
+ function extractKeywords(prompt: string): string[] {
290
+ const stopWords = new Set([
291
+ 'a',
292
+ 'an',
293
+ 'the',
294
+ 'to',
295
+ 'on',
296
+ 'in',
297
+ 'for',
298
+ 'is',
299
+ 'it',
300
+ 'when',
301
+ 'i',
302
+ 'want',
303
+ 'need',
304
+ 'create',
305
+ 'make',
306
+ 'please',
307
+ 'can',
308
+ 'you',
309
+ 'would',
310
+ 'should',
311
+ 'like',
312
+ 'that',
313
+ 'this',
314
+ 'with',
315
+ 'from',
316
+ ]);
317
+
318
+ return prompt
319
+ .toLowerCase()
320
+ .split(/\W+/)
321
+ .filter(word => word.length > 2 && !stopWords.has(word));
322
+ }
323
+
324
+ /**
325
+ * Track usage of examples.
326
+ */
327
+ function trackUsage(db: any, ids: number[]): void {
328
+ if (ids.length === 0) return;
329
+
330
+ try {
331
+ const stmt = db.prepare(`
332
+ UPDATE llm_examples SET usage_count = usage_count + 1 WHERE id = ?
333
+ `);
334
+
335
+ for (const id of ids) {
336
+ stmt.run(id);
337
+ }
338
+ } catch {
339
+ // Silently ignore tracking errors
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Map database row to LLMExample type.
345
+ */
346
+ function mapRowToLLMExample(row: LLMExampleRow): LLMExample {
347
+ return {
348
+ id: row.id,
349
+ patternId: row.code_example_id,
350
+ language: row.language,
351
+ prompt: row.prompt,
352
+ completion: row.completion,
353
+ qualityScore: row.quality_score,
354
+ usageCount: row.usage_count,
355
+ createdAt: new Date(row.created_at),
356
+ };
357
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Pattern Queries API
3
+ *
4
+ * Provides functions to query patterns from the database.
5
+ */
6
+
7
+ import { getDatabase } from '../database/connection';
8
+ import type {
9
+ Pattern,
10
+ SearchOptions,
11
+ PatternStats,
12
+ ConnectionOptions,
13
+ EngineCompat,
14
+ } from '../types';
15
+
16
+ // =============================================================================
17
+ // Database Row Types
18
+ // =============================================================================
19
+
20
+ interface CodeExampleRow {
21
+ id: string;
22
+ title: string;
23
+ raw_code: string;
24
+ description: string | null;
25
+ feature: string | null;
26
+ engine: string | null;
27
+ source_url: string | null;
28
+ created_at: string;
29
+ }
30
+
31
+ // =============================================================================
32
+ // Query Functions
33
+ // =============================================================================
34
+
35
+ /**
36
+ * Get a pattern by ID.
37
+ */
38
+ export async function getPatternById(
39
+ id: string,
40
+ options?: ConnectionOptions
41
+ ): Promise<Pattern | null> {
42
+ const db = getDatabase({ ...options, readonly: true });
43
+ const row = db
44
+ .prepare(
45
+ `
46
+ SELECT id, title, raw_code, description, feature, engine, created_at
47
+ FROM code_examples
48
+ WHERE id = ?
49
+ `
50
+ )
51
+ .get(id) as CodeExampleRow | undefined;
52
+
53
+ return row ? mapRowToPattern(row) : null;
54
+ }
55
+
56
+ /**
57
+ * Get patterns by category (feature in hyperscript-lsp schema).
58
+ */
59
+ export async function getPatternsByCategory(
60
+ category: string,
61
+ options?: ConnectionOptions
62
+ ): Promise<Pattern[]> {
63
+ const db = getDatabase({ ...options, readonly: true });
64
+ const rows = db
65
+ .prepare(
66
+ `
67
+ SELECT id, title, raw_code, description, feature, engine, created_at
68
+ FROM code_examples
69
+ WHERE feature = ?
70
+ ORDER BY title
71
+ `
72
+ )
73
+ .all(category) as CodeExampleRow[];
74
+
75
+ return rows.map(mapRowToPattern);
76
+ }
77
+
78
+ /**
79
+ * Get patterns by command (extracted from raw_code).
80
+ */
81
+ export async function getPatternsByCommand(
82
+ command: string,
83
+ options?: ConnectionOptions
84
+ ): Promise<Pattern[]> {
85
+ const db = getDatabase({ ...options, readonly: true });
86
+ // Use word boundary regex in SQLite
87
+ const rows = db
88
+ .prepare(
89
+ `
90
+ SELECT id, title, raw_code, description, feature, engine, created_at
91
+ FROM code_examples
92
+ WHERE raw_code LIKE ?
93
+ ORDER BY title
94
+ `
95
+ )
96
+ .all(`%${command}%`) as CodeExampleRow[];
97
+
98
+ // Filter more precisely in JS (SQLite LIKE is case-insensitive and broad)
99
+ return rows
100
+ .filter(row => new RegExp(`\\b${command}\\b`, 'i').test(row.raw_code))
101
+ .map(mapRowToPattern);
102
+ }
103
+
104
+ /**
105
+ * Search patterns by text query.
106
+ */
107
+ export async function searchPatterns(
108
+ query: string,
109
+ searchOptions: SearchOptions = {},
110
+ connOptions?: ConnectionOptions
111
+ ): Promise<Pattern[]> {
112
+ const db = getDatabase({ ...connOptions, readonly: true });
113
+ const { limit = 50, offset = 0 } = searchOptions;
114
+
115
+ const rows = db
116
+ .prepare(
117
+ `
118
+ SELECT id, title, raw_code, description, feature, engine, created_at
119
+ FROM code_examples
120
+ WHERE title LIKE ? OR raw_code LIKE ? OR description LIKE ?
121
+ ORDER BY title
122
+ LIMIT ? OFFSET ?
123
+ `
124
+ )
125
+ .all(`%${query}%`, `%${query}%`, `%${query}%`, limit, offset) as CodeExampleRow[];
126
+
127
+ return rows.map(mapRowToPattern);
128
+ }
129
+
130
+ /**
131
+ * Get all patterns.
132
+ */
133
+ export async function getAllPatterns(
134
+ searchOptions: SearchOptions = {},
135
+ connOptions?: ConnectionOptions
136
+ ): Promise<Pattern[]> {
137
+ const db = getDatabase({ ...connOptions, readonly: true });
138
+ const { limit = 1000, offset = 0 } = searchOptions;
139
+
140
+ const rows = db
141
+ .prepare(
142
+ `
143
+ SELECT id, title, raw_code, description, feature, engine, created_at
144
+ FROM code_examples
145
+ ORDER BY title
146
+ LIMIT ? OFFSET ?
147
+ `
148
+ )
149
+ .all(limit, offset) as CodeExampleRow[];
150
+
151
+ return rows.map(mapRowToPattern);
152
+ }
153
+
154
+ /**
155
+ * Get pattern statistics.
156
+ */
157
+ export async function getPatternStats(connOptions?: ConnectionOptions): Promise<PatternStats> {
158
+ const db = getDatabase({ ...connOptions, readonly: true });
159
+
160
+ // Total patterns
161
+ const patternCount = db.prepare('SELECT COUNT(*) as count FROM code_examples').get() as {
162
+ count: number;
163
+ };
164
+
165
+ // Total translations
166
+ const translationCount = db
167
+ .prepare('SELECT COUNT(*) as count FROM pattern_translations')
168
+ .get() as {
169
+ count: number;
170
+ };
171
+
172
+ // By language
173
+ const byLangRows = db
174
+ .prepare(
175
+ `
176
+ SELECT
177
+ language,
178
+ COUNT(*) as count,
179
+ SUM(verified_parses) as verified_count
180
+ FROM pattern_translations
181
+ GROUP BY language
182
+ `
183
+ )
184
+ .all() as { language: string; count: number; verified_count: number }[];
185
+
186
+ const byLanguage: Record<string, { count: number; verifiedCount: number }> = {};
187
+ for (const row of byLangRows) {
188
+ byLanguage[row.language] = {
189
+ count: row.count,
190
+ verifiedCount: row.verified_count,
191
+ };
192
+ }
193
+
194
+ // By category
195
+ const byCatRows = db
196
+ .prepare(
197
+ `
198
+ SELECT feature, COUNT(*) as count
199
+ FROM code_examples
200
+ WHERE feature IS NOT NULL
201
+ GROUP BY feature
202
+ `
203
+ )
204
+ .all() as { feature: string; count: number }[];
205
+
206
+ const byCategory: Record<string, number> = {};
207
+ for (const row of byCatRows) {
208
+ byCategory[row.feature] = row.count;
209
+ }
210
+
211
+ // Average confidence
212
+ const avgConfResult = db
213
+ .prepare('SELECT AVG(confidence) as avg FROM pattern_translations WHERE confidence > 0')
214
+ .get() as { avg: number };
215
+
216
+ return {
217
+ totalPatterns: patternCount.count,
218
+ totalTranslations: translationCount.count,
219
+ byLanguage,
220
+ byCategory,
221
+ avgConfidence: avgConfResult.avg || 0,
222
+ };
223
+ }
224
+
225
+ // =============================================================================
226
+ // Helper Functions
227
+ // =============================================================================
228
+
229
+ /**
230
+ * Map database row to Pattern type.
231
+ */
232
+ function mapRowToPattern(row: CodeExampleRow): Pattern {
233
+ return {
234
+ id: row.id,
235
+ title: row.title,
236
+ description: row.description,
237
+ rawCode: row.raw_code,
238
+ category: row.feature,
239
+ primaryCommand: extractPrimaryCommand(row.raw_code),
240
+ tags: extractTags(row.raw_code),
241
+ difficulty: inferDifficulty(row.raw_code),
242
+ engine: (row.engine as EngineCompat) || null,
243
+ createdAt: new Date(row.created_at),
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Extract primary command from hyperscript code.
249
+ */
250
+ function extractPrimaryCommand(code: string): string | null {
251
+ const match = code.match(/^(on|toggle|put|set|add|remove|show|hide|wait|log|send|fetch|call)\b/i);
252
+ return match ? match[1].toLowerCase() : null;
253
+ }
254
+
255
+ /**
256
+ * Extract tags from code.
257
+ */
258
+ function extractTags(code: string): string[] {
259
+ const tags: string[] = [];
260
+ if (code.includes('.')) tags.push('class');
261
+ if (code.includes('#')) tags.push('id');
262
+ if (code.includes('on ')) tags.push('event');
263
+ if (code.includes('fetch')) tags.push('async');
264
+ if (code.includes('wait')) tags.push('timing');
265
+ return tags;
266
+ }
267
+
268
+ /**
269
+ * Infer difficulty from code complexity.
270
+ */
271
+ function inferDifficulty(code: string): 'beginner' | 'intermediate' | 'advanced' {
272
+ const lines = code.split('\n').filter(l => l.trim()).length;
273
+ if (lines === 1 && !code.includes('then')) return 'beginner';
274
+ if (lines <= 3) return 'intermediate';
275
+ return 'advanced';
276
+ }