capman 0.2.0 → 0.3.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/bin/capman.js +151 -5
- package/dist/cjs/cache.d.ts +42 -0
- package/dist/cjs/cache.d.ts.map +1 -0
- package/dist/cjs/cache.js +181 -0
- package/dist/cjs/cache.js.map +1 -0
- package/dist/cjs/engine.d.ts +82 -0
- package/dist/cjs/engine.d.ts.map +1 -0
- package/dist/cjs/engine.js +154 -0
- package/dist/cjs/engine.js.map +1 -0
- package/dist/cjs/generator.js +2 -1
- package/dist/cjs/generator.js.map +1 -1
- package/dist/cjs/index.d.ts +7 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +13 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/learning.d.ts +56 -0
- package/dist/cjs/learning.d.ts.map +1 -0
- package/dist/cjs/learning.js +184 -0
- package/dist/cjs/learning.js.map +1 -0
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +27 -5
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +36 -4
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/schema.d.ts +10 -10
- package/dist/cjs/types.d.ts +12 -5
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/version.d.ts +2 -0
- package/dist/cjs/version.d.ts.map +1 -0
- package/dist/cjs/version.js +6 -0
- package/dist/cjs/version.js.map +1 -0
- package/dist/esm/cache.js +141 -0
- package/dist/esm/engine.js +149 -0
- package/dist/esm/generator.js +2 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/learning.js +145 -0
- package/dist/esm/matcher.js +27 -5
- package/dist/esm/resolver.js +36 -4
- package/dist/esm/version.js +2 -0
- package/package.json +18 -12
package/dist/cjs/schema.d.ts
CHANGED
|
@@ -146,6 +146,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
146
146
|
}>;
|
|
147
147
|
}, "strip", z.ZodTypeAny, {
|
|
148
148
|
name: string;
|
|
149
|
+
id: string;
|
|
149
150
|
params: {
|
|
150
151
|
name: string;
|
|
151
152
|
required: boolean;
|
|
@@ -155,7 +156,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
155
156
|
}[];
|
|
156
157
|
description: string;
|
|
157
158
|
returns: string[];
|
|
158
|
-
id: string;
|
|
159
159
|
resolver: {
|
|
160
160
|
type: "api";
|
|
161
161
|
endpoints: {
|
|
@@ -188,6 +188,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
188
188
|
examples?: string[] | undefined;
|
|
189
189
|
}, {
|
|
190
190
|
name: string;
|
|
191
|
+
id: string;
|
|
191
192
|
params: {
|
|
192
193
|
name: string;
|
|
193
194
|
required: boolean;
|
|
@@ -197,7 +198,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
197
198
|
}[];
|
|
198
199
|
description: string;
|
|
199
200
|
returns: string[];
|
|
200
|
-
id: string;
|
|
201
201
|
resolver: {
|
|
202
202
|
type: "api";
|
|
203
203
|
endpoints: {
|
|
@@ -230,6 +230,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
230
230
|
examples?: string[] | undefined;
|
|
231
231
|
}>, "many">, {
|
|
232
232
|
name: string;
|
|
233
|
+
id: string;
|
|
233
234
|
params: {
|
|
234
235
|
name: string;
|
|
235
236
|
required: boolean;
|
|
@@ -239,7 +240,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
239
240
|
}[];
|
|
240
241
|
description: string;
|
|
241
242
|
returns: string[];
|
|
242
|
-
id: string;
|
|
243
243
|
resolver: {
|
|
244
244
|
type: "api";
|
|
245
245
|
endpoints: {
|
|
@@ -272,6 +272,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
272
272
|
examples?: string[] | undefined;
|
|
273
273
|
}[], {
|
|
274
274
|
name: string;
|
|
275
|
+
id: string;
|
|
275
276
|
params: {
|
|
276
277
|
name: string;
|
|
277
278
|
required: boolean;
|
|
@@ -281,7 +282,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
281
282
|
}[];
|
|
282
283
|
description: string;
|
|
283
284
|
returns: string[];
|
|
284
|
-
id: string;
|
|
285
285
|
resolver: {
|
|
286
286
|
type: "api";
|
|
287
287
|
endpoints: {
|
|
@@ -317,6 +317,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
317
317
|
app: string;
|
|
318
318
|
capabilities: {
|
|
319
319
|
name: string;
|
|
320
|
+
id: string;
|
|
320
321
|
params: {
|
|
321
322
|
name: string;
|
|
322
323
|
required: boolean;
|
|
@@ -326,7 +327,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
326
327
|
}[];
|
|
327
328
|
description: string;
|
|
328
329
|
returns: string[];
|
|
329
|
-
id: string;
|
|
330
330
|
resolver: {
|
|
331
331
|
type: "api";
|
|
332
332
|
endpoints: {
|
|
@@ -363,6 +363,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
363
363
|
app: string;
|
|
364
364
|
capabilities: {
|
|
365
365
|
name: string;
|
|
366
|
+
id: string;
|
|
366
367
|
params: {
|
|
367
368
|
name: string;
|
|
368
369
|
required: boolean;
|
|
@@ -372,7 +373,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
372
373
|
}[];
|
|
373
374
|
description: string;
|
|
374
375
|
returns: string[];
|
|
375
|
-
id: string;
|
|
376
376
|
resolver: {
|
|
377
377
|
type: "api";
|
|
378
378
|
endpoints: {
|
|
@@ -554,6 +554,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
554
554
|
}>;
|
|
555
555
|
}, "strip", z.ZodTypeAny, {
|
|
556
556
|
name: string;
|
|
557
|
+
id: string;
|
|
557
558
|
params: {
|
|
558
559
|
name: string;
|
|
559
560
|
required: boolean;
|
|
@@ -563,7 +564,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
563
564
|
}[];
|
|
564
565
|
description: string;
|
|
565
566
|
returns: string[];
|
|
566
|
-
id: string;
|
|
567
567
|
resolver: {
|
|
568
568
|
type: "api";
|
|
569
569
|
endpoints: {
|
|
@@ -596,6 +596,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
596
596
|
examples?: string[] | undefined;
|
|
597
597
|
}, {
|
|
598
598
|
name: string;
|
|
599
|
+
id: string;
|
|
599
600
|
params: {
|
|
600
601
|
name: string;
|
|
601
602
|
required: boolean;
|
|
@@ -605,7 +606,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
605
606
|
}[];
|
|
606
607
|
description: string;
|
|
607
608
|
returns: string[];
|
|
608
|
-
id: string;
|
|
609
609
|
resolver: {
|
|
610
610
|
type: "api";
|
|
611
611
|
endpoints: {
|
|
@@ -642,6 +642,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
642
642
|
app: string;
|
|
643
643
|
capabilities: {
|
|
644
644
|
name: string;
|
|
645
|
+
id: string;
|
|
645
646
|
params: {
|
|
646
647
|
name: string;
|
|
647
648
|
required: boolean;
|
|
@@ -651,7 +652,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
651
652
|
}[];
|
|
652
653
|
description: string;
|
|
653
654
|
returns: string[];
|
|
654
|
-
id: string;
|
|
655
655
|
resolver: {
|
|
656
656
|
type: "api";
|
|
657
657
|
endpoints: {
|
|
@@ -689,6 +689,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
689
689
|
app: string;
|
|
690
690
|
capabilities: {
|
|
691
691
|
name: string;
|
|
692
|
+
id: string;
|
|
692
693
|
params: {
|
|
693
694
|
name: string;
|
|
694
695
|
required: boolean;
|
|
@@ -698,7 +699,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
698
699
|
}[];
|
|
699
700
|
description: string;
|
|
700
701
|
returns: string[];
|
|
701
|
-
id: string;
|
|
702
702
|
resolver: {
|
|
703
703
|
type: "api";
|
|
704
704
|
endpoints: {
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -58,15 +58,22 @@ export interface MatchResult {
|
|
|
58
58
|
extractedParams: Record<string, string | null>;
|
|
59
59
|
reasoning: string;
|
|
60
60
|
}
|
|
61
|
+
export interface ApiCallResult {
|
|
62
|
+
method: string;
|
|
63
|
+
url: string;
|
|
64
|
+
params: Record<string, unknown>;
|
|
65
|
+
/** HTTP status code — only present when actually executed (not dry run) */
|
|
66
|
+
status?: number;
|
|
67
|
+
/** Parsed JSON response body — only present when actually executed */
|
|
68
|
+
data?: unknown;
|
|
69
|
+
}
|
|
61
70
|
export interface ResolveResult {
|
|
62
71
|
success: boolean;
|
|
63
72
|
resolverType: ResolverType | null;
|
|
64
|
-
apiCalls?:
|
|
65
|
-
method: string;
|
|
66
|
-
url: string;
|
|
67
|
-
params: Record<string, unknown>;
|
|
68
|
-
}>;
|
|
73
|
+
apiCalls?: ApiCallResult[];
|
|
69
74
|
navTarget?: string;
|
|
75
|
+
/** Execution time in milliseconds */
|
|
76
|
+
durationMs?: number;
|
|
70
77
|
error?: string;
|
|
71
78
|
}
|
|
72
79
|
export interface ValidationResult {
|
package/dist/cjs/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AACnD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAIpE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IACvD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CACpC;AAID,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAA;IACX,SAAS,EAAE,KAAK,CAAC;QACf,MAAM,EAAE,UAAU,CAAA;QAClB,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAClB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAC9B,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAA;AAIjE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAA;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAID,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,YAAY,CAAA;CACtB;AAID,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,UAAU,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,UAAU,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,cAAc,CAAA;IAC9D,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;IAC9C,SAAS,EAAE,MAAM,CAAA;CAClB;AAID,MAAM,WAAW,aAAa;IAC5B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":";;;AAAA,8DAA8D;AACjD,QAAA,OAAO,GAAG,OAAO,CAAA"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { logger } from './logger';
|
|
4
|
+
// ─── Normalize query for cache key ────────────────────────────────────────────
|
|
5
|
+
function normalizeQuery(query) {
|
|
6
|
+
return query.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
7
|
+
}
|
|
8
|
+
// ─── Memory Cache ─────────────────────────────────────────────────────────────
|
|
9
|
+
export class MemoryCache {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.store = new Map();
|
|
12
|
+
}
|
|
13
|
+
async get(query) {
|
|
14
|
+
const key = normalizeQuery(query);
|
|
15
|
+
const entry = this.store.get(key);
|
|
16
|
+
if (entry) {
|
|
17
|
+
entry.hits++;
|
|
18
|
+
logger.debug(`Cache hit (memory): "${query}"`);
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
async set(query, result) {
|
|
24
|
+
const key = normalizeQuery(query);
|
|
25
|
+
this.store.set(key, {
|
|
26
|
+
query,
|
|
27
|
+
result,
|
|
28
|
+
cachedAt: new Date().toISOString(),
|
|
29
|
+
hits: 0,
|
|
30
|
+
});
|
|
31
|
+
logger.debug(`Cache set (memory): "${query}"`);
|
|
32
|
+
}
|
|
33
|
+
async clear() {
|
|
34
|
+
this.store.clear();
|
|
35
|
+
}
|
|
36
|
+
async size() {
|
|
37
|
+
return this.store.size;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ─── File Cache ───────────────────────────────────────────────────────────────
|
|
41
|
+
export class FileCache {
|
|
42
|
+
constructor(filePath = '.capman/cache.json') {
|
|
43
|
+
this.store = new Map();
|
|
44
|
+
this.loaded = false;
|
|
45
|
+
this.filePath = path.resolve(process.cwd(), filePath);
|
|
46
|
+
}
|
|
47
|
+
load() {
|
|
48
|
+
if (this.loaded)
|
|
49
|
+
return;
|
|
50
|
+
try {
|
|
51
|
+
if (fs.existsSync(this.filePath)) {
|
|
52
|
+
const raw = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
53
|
+
this.store = new Map(Object.entries(raw));
|
|
54
|
+
logger.debug(`File cache loaded: ${this.store.size} entries`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
logger.warn(`Failed to load file cache at ${this.filePath}`);
|
|
59
|
+
}
|
|
60
|
+
this.loaded = true;
|
|
61
|
+
}
|
|
62
|
+
save() {
|
|
63
|
+
try {
|
|
64
|
+
const dir = path.dirname(this.filePath);
|
|
65
|
+
if (!fs.existsSync(dir))
|
|
66
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
67
|
+
const obj = Object.fromEntries(this.store);
|
|
68
|
+
fs.writeFileSync(this.filePath, JSON.stringify(obj, null, 2));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
logger.warn(`Failed to save file cache to ${this.filePath}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async get(query) {
|
|
75
|
+
this.load();
|
|
76
|
+
const key = normalizeQuery(query);
|
|
77
|
+
const entry = this.store.get(key);
|
|
78
|
+
if (entry) {
|
|
79
|
+
entry.hits++;
|
|
80
|
+
logger.debug(`Cache hit (file): "${query}"`);
|
|
81
|
+
return entry;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
async set(query, result) {
|
|
86
|
+
this.load();
|
|
87
|
+
const key = normalizeQuery(query);
|
|
88
|
+
this.store.set(key, {
|
|
89
|
+
query,
|
|
90
|
+
result,
|
|
91
|
+
cachedAt: new Date().toISOString(),
|
|
92
|
+
hits: 0,
|
|
93
|
+
});
|
|
94
|
+
this.save();
|
|
95
|
+
logger.debug(`Cache set (file): "${query}"`);
|
|
96
|
+
}
|
|
97
|
+
async clear() {
|
|
98
|
+
this.store.clear();
|
|
99
|
+
this.save();
|
|
100
|
+
}
|
|
101
|
+
async size() {
|
|
102
|
+
this.load();
|
|
103
|
+
return this.store.size;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// ─── Combo Cache (memory first, file fallback) ────────────────────────────────
|
|
107
|
+
export class ComboCache {
|
|
108
|
+
constructor(filePath = '.capman/cache.json') {
|
|
109
|
+
this.memory = new MemoryCache();
|
|
110
|
+
this.file = new FileCache(filePath);
|
|
111
|
+
}
|
|
112
|
+
async get(query) {
|
|
113
|
+
// Memory first — fastest
|
|
114
|
+
const memHit = await this.memory.get(query);
|
|
115
|
+
if (memHit)
|
|
116
|
+
return memHit;
|
|
117
|
+
// File fallback — persists across restarts
|
|
118
|
+
const fileHit = await this.file.get(query);
|
|
119
|
+
if (fileHit) {
|
|
120
|
+
// Promote to memory for next time
|
|
121
|
+
await this.memory.set(query, fileHit.result);
|
|
122
|
+
return fileHit;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
async set(query, result) {
|
|
127
|
+
await Promise.all([
|
|
128
|
+
this.memory.set(query, result),
|
|
129
|
+
this.file.set(query, result),
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
async clear() {
|
|
133
|
+
await Promise.all([
|
|
134
|
+
this.memory.clear(),
|
|
135
|
+
this.file.clear(),
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
async size() {
|
|
139
|
+
return this.file.size();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { match as _match, matchWithLLM as _matchWithLLM } from './matcher';
|
|
2
|
+
import { resolve as _resolve } from './resolver';
|
|
3
|
+
import { ComboCache } from './cache';
|
|
4
|
+
import { FileLearningStore } from './learning';
|
|
5
|
+
import { logger } from './logger';
|
|
6
|
+
// ─── CapmanEngine ─────────────────────────────────────────────────────────────
|
|
7
|
+
export class CapmanEngine {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.manifest = options.manifest;
|
|
10
|
+
this.mode = options.mode ?? 'balanced';
|
|
11
|
+
this.llm = options.llm;
|
|
12
|
+
this.baseUrl = options.baseUrl;
|
|
13
|
+
this.auth = options.auth;
|
|
14
|
+
this.headers = options.headers;
|
|
15
|
+
this.threshold = options.threshold ?? 50;
|
|
16
|
+
// Cache — default ComboCache, or disabled with false
|
|
17
|
+
this.cache = options.cache === false
|
|
18
|
+
? null
|
|
19
|
+
: (options.cache ?? new ComboCache());
|
|
20
|
+
// Learning — default FileLearningStore, or disabled with false
|
|
21
|
+
this.learning = options.learning === false
|
|
22
|
+
? null
|
|
23
|
+
: (options.learning ?? new FileLearningStore());
|
|
24
|
+
logger.info(`CapmanEngine initialized — mode: ${this.mode}, cache: ${this.cache ? 'enabled' : 'disabled'}, learning: ${this.learning ? 'enabled' : 'disabled'}`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Ask the engine a natural language query.
|
|
28
|
+
* Automatically handles caching, matching, resolution, and learning.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const engine = new CapmanEngine({ manifest, llm: myLLM })
|
|
32
|
+
* const result = await engine.ask("Check availability for blue jacket")
|
|
33
|
+
* console.log(result.match.capability?.id) // check_product_availability
|
|
34
|
+
* console.log(result.resolution.apiCalls) // [{ url: '...', method: 'GET' }]
|
|
35
|
+
* console.log(result.resolvedVia) // 'keyword' | 'llm' | 'cache'
|
|
36
|
+
*/
|
|
37
|
+
async ask(query, overrides = {}) {
|
|
38
|
+
const start = Date.now();
|
|
39
|
+
// ── Step 1: Check cache ──────────────────────────────────────────────────
|
|
40
|
+
if (this.cache) {
|
|
41
|
+
const cached = await this.cache.get(query);
|
|
42
|
+
if (cached) {
|
|
43
|
+
logger.info(`Cache hit for: "${query}"`);
|
|
44
|
+
const resolution = await _resolve(cached.result, cached.result.extractedParams, this.resolveOptions(overrides));
|
|
45
|
+
const result = {
|
|
46
|
+
match: cached.result,
|
|
47
|
+
resolution,
|
|
48
|
+
resolvedVia: 'cache',
|
|
49
|
+
durationMs: Date.now() - start,
|
|
50
|
+
};
|
|
51
|
+
await this.recordLearning(query, cached.result, 'cache');
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ── Step 2: Match ────────────────────────────────────────────────────────
|
|
56
|
+
let matchResult;
|
|
57
|
+
let resolvedVia = 'keyword';
|
|
58
|
+
switch (this.mode) {
|
|
59
|
+
case 'cheap': {
|
|
60
|
+
matchResult = _match(query, this.manifest);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case 'accurate': {
|
|
64
|
+
if (this.llm) {
|
|
65
|
+
matchResult = await _matchWithLLM(query, this.manifest, { llm: this.llm });
|
|
66
|
+
resolvedVia = 'llm';
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
logger.warn('accurate mode requires llm — falling back to keyword');
|
|
70
|
+
matchResult = _match(query, this.manifest);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'balanced':
|
|
75
|
+
default: {
|
|
76
|
+
const keywordResult = _match(query, this.manifest);
|
|
77
|
+
if (keywordResult.confidence >= this.threshold || !this.llm) {
|
|
78
|
+
matchResult = keywordResult;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
logger.info(`Low confidence (${keywordResult.confidence}%) — escalating to LLM`);
|
|
82
|
+
matchResult = await _matchWithLLM(query, this.manifest, { llm: this.llm });
|
|
83
|
+
resolvedVia = 'llm';
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ── Step 3: Cache the match result ───────────────────────────────────────
|
|
89
|
+
if (this.cache && matchResult.capability) {
|
|
90
|
+
await this.cache.set(query, matchResult);
|
|
91
|
+
}
|
|
92
|
+
// ── Step 4: Resolve ──────────────────────────────────────────────────────
|
|
93
|
+
const resolution = await _resolve(matchResult, matchResult.extractedParams, this.resolveOptions(overrides));
|
|
94
|
+
// ── Step 5: Record learning ──────────────────────────────────────────────
|
|
95
|
+
await this.recordLearning(query, matchResult, resolvedVia);
|
|
96
|
+
return {
|
|
97
|
+
match: matchResult,
|
|
98
|
+
resolution,
|
|
99
|
+
resolvedVia,
|
|
100
|
+
durationMs: Date.now() - start,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get stats from the learning store.
|
|
105
|
+
* Shows which capabilities are most used, LLM vs keyword ratio, cache hit rate.
|
|
106
|
+
*/
|
|
107
|
+
async getStats() {
|
|
108
|
+
if (!this.learning)
|
|
109
|
+
return null;
|
|
110
|
+
return this.learning.getStats();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get the most frequently matched capabilities.
|
|
114
|
+
*/
|
|
115
|
+
async getTopCapabilities(limit = 5) {
|
|
116
|
+
if (!this.learning)
|
|
117
|
+
return [];
|
|
118
|
+
return this.learning.getTopCapabilities(limit);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Clear the cache.
|
|
122
|
+
*/
|
|
123
|
+
async clearCache() {
|
|
124
|
+
if (this.cache)
|
|
125
|
+
await this.cache.clear();
|
|
126
|
+
}
|
|
127
|
+
// ── Private helpers ────────────────────────────────────────────────────────
|
|
128
|
+
resolveOptions(overrides = {}) {
|
|
129
|
+
return {
|
|
130
|
+
baseUrl: this.baseUrl,
|
|
131
|
+
auth: this.auth,
|
|
132
|
+
headers: this.headers,
|
|
133
|
+
...overrides,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async recordLearning(query, matchResult, resolvedVia) {
|
|
137
|
+
if (!this.learning)
|
|
138
|
+
return;
|
|
139
|
+
await this.learning.record({
|
|
140
|
+
query,
|
|
141
|
+
capabilityId: matchResult.capability?.id ?? null,
|
|
142
|
+
confidence: matchResult.confidence,
|
|
143
|
+
intent: matchResult.intent,
|
|
144
|
+
extractedParams: matchResult.extractedParams,
|
|
145
|
+
resolvedVia,
|
|
146
|
+
timestamp: new Date().toISOString(),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
package/dist/esm/generator.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { VERSION } from './version';
|
|
1
2
|
import * as fs from 'fs';
|
|
2
3
|
import * as path from 'path';
|
|
3
4
|
import { validateConfig, validateManifest } from './schema';
|
|
4
5
|
import { logger } from './logger';
|
|
5
6
|
export function generate(config) {
|
|
6
7
|
return {
|
|
7
|
-
version:
|
|
8
|
+
version: VERSION,
|
|
8
9
|
app: config.app,
|
|
9
10
|
generatedAt: new Date().toISOString(),
|
|
10
11
|
capabilities: config.capabilities,
|
package/dist/esm/index.js
CHANGED
|
@@ -6,6 +6,12 @@ export { resolve } from './resolver';
|
|
|
6
6
|
// ─── Convenience: ask() — match + resolve in one call ────────────────────────
|
|
7
7
|
import { match as _match, matchWithLLM as _matchWithLLM } from './matcher';
|
|
8
8
|
import { resolve as _resolve } from './resolver';
|
|
9
|
+
// ─── Engine (recommended API) ─────────────────────────────────────────────────
|
|
10
|
+
export { CapmanEngine } from './engine';
|
|
11
|
+
// ─── Cache ────────────────────────────────────────────────────────────────────
|
|
12
|
+
export { MemoryCache, FileCache, ComboCache } from './cache';
|
|
13
|
+
// ─── Learning ─────────────────────────────────────────────────────────────────
|
|
14
|
+
export { FileLearningStore, MemoryLearningStore } from './learning';
|
|
9
15
|
/**
|
|
10
16
|
* One-shot convenience: match + resolve in a single call.
|
|
11
17
|
*
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { logger } from './logger';
|
|
4
|
+
// ─── File Learning Store ──────────────────────────────────────────────────────
|
|
5
|
+
export class FileLearningStore {
|
|
6
|
+
constructor(filePath = '.capman/learning.json') {
|
|
7
|
+
this.entries = [];
|
|
8
|
+
this.loaded = false;
|
|
9
|
+
this.filePath = path.resolve(process.cwd(), filePath);
|
|
10
|
+
}
|
|
11
|
+
load() {
|
|
12
|
+
if (this.loaded)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(this.filePath)) {
|
|
16
|
+
const raw = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
17
|
+
this.entries = raw.entries ?? [];
|
|
18
|
+
logger.debug(`Learning store loaded: ${this.entries.length} entries`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
logger.warn(`Failed to load learning store at ${this.filePath}`);
|
|
23
|
+
}
|
|
24
|
+
this.loaded = true;
|
|
25
|
+
}
|
|
26
|
+
save() {
|
|
27
|
+
try {
|
|
28
|
+
const dir = path.dirname(this.filePath);
|
|
29
|
+
if (!fs.existsSync(dir))
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
fs.writeFileSync(this.filePath, JSON.stringify({
|
|
32
|
+
entries: this.entries,
|
|
33
|
+
updatedAt: new Date().toISOString(),
|
|
34
|
+
}, null, 2));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
logger.warn(`Failed to save learning store to ${this.filePath}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async record(entry) {
|
|
41
|
+
this.load();
|
|
42
|
+
this.entries.push(entry);
|
|
43
|
+
this.save();
|
|
44
|
+
logger.debug(`Learning recorded: "${entry.query}" → ${entry.capabilityId ?? 'OUT_OF_SCOPE'} via ${entry.resolvedVia}`);
|
|
45
|
+
}
|
|
46
|
+
async getStats() {
|
|
47
|
+
this.load();
|
|
48
|
+
const index = {};
|
|
49
|
+
let totalQueries = 0;
|
|
50
|
+
let llmQueries = 0;
|
|
51
|
+
let cacheHits = 0;
|
|
52
|
+
let outOfScope = 0;
|
|
53
|
+
for (const entry of this.entries) {
|
|
54
|
+
totalQueries++;
|
|
55
|
+
if (entry.resolvedVia === 'llm')
|
|
56
|
+
llmQueries++;
|
|
57
|
+
if (entry.resolvedVia === 'cache')
|
|
58
|
+
cacheHits++;
|
|
59
|
+
if (!entry.capabilityId)
|
|
60
|
+
outOfScope++;
|
|
61
|
+
if (entry.capabilityId) {
|
|
62
|
+
// Index each word of the query against the matched capability
|
|
63
|
+
const words = entry.query.toLowerCase()
|
|
64
|
+
.split(/\W+/)
|
|
65
|
+
.filter(w => w.length > 2);
|
|
66
|
+
for (const word of words) {
|
|
67
|
+
if (!index[word])
|
|
68
|
+
index[word] = {};
|
|
69
|
+
index[word][entry.capabilityId] =
|
|
70
|
+
(index[word][entry.capabilityId] ?? 0) + 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { index, totalQueries, llmQueries, cacheHits, outOfScope };
|
|
75
|
+
}
|
|
76
|
+
async getTopCapabilities(limit = 5) {
|
|
77
|
+
this.load();
|
|
78
|
+
const counts = {};
|
|
79
|
+
for (const entry of this.entries) {
|
|
80
|
+
if (entry.capabilityId) {
|
|
81
|
+
counts[entry.capabilityId] = (counts[entry.capabilityId] ?? 0) + 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return Object.entries(counts)
|
|
85
|
+
.sort(([, a], [, b]) => b - a)
|
|
86
|
+
.slice(0, limit)
|
|
87
|
+
.map(([id, hits]) => ({ id, hits }));
|
|
88
|
+
}
|
|
89
|
+
async clear() {
|
|
90
|
+
this.entries = [];
|
|
91
|
+
this.save();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ─── Memory Learning Store (for testing) ─────────────────────────────────────
|
|
95
|
+
export class MemoryLearningStore {
|
|
96
|
+
constructor() {
|
|
97
|
+
this.entries = [];
|
|
98
|
+
}
|
|
99
|
+
async record(entry) {
|
|
100
|
+
this.entries.push(entry);
|
|
101
|
+
}
|
|
102
|
+
async getStats() {
|
|
103
|
+
const index = {};
|
|
104
|
+
let totalQueries = 0;
|
|
105
|
+
let llmQueries = 0;
|
|
106
|
+
let cacheHits = 0;
|
|
107
|
+
let outOfScope = 0;
|
|
108
|
+
for (const entry of this.entries) {
|
|
109
|
+
totalQueries++;
|
|
110
|
+
if (entry.resolvedVia === 'llm')
|
|
111
|
+
llmQueries++;
|
|
112
|
+
if (entry.resolvedVia === 'cache')
|
|
113
|
+
cacheHits++;
|
|
114
|
+
if (!entry.capabilityId)
|
|
115
|
+
outOfScope++;
|
|
116
|
+
if (entry.capabilityId) {
|
|
117
|
+
const words = entry.query.toLowerCase()
|
|
118
|
+
.split(/\W+/)
|
|
119
|
+
.filter(w => w.length > 2);
|
|
120
|
+
for (const word of words) {
|
|
121
|
+
if (!index[word])
|
|
122
|
+
index[word] = {};
|
|
123
|
+
index[word][entry.capabilityId] =
|
|
124
|
+
(index[word][entry.capabilityId] ?? 0) + 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { index, totalQueries, llmQueries, cacheHits, outOfScope };
|
|
129
|
+
}
|
|
130
|
+
async getTopCapabilities(limit = 5) {
|
|
131
|
+
const counts = {};
|
|
132
|
+
for (const entry of this.entries) {
|
|
133
|
+
if (entry.capabilityId) {
|
|
134
|
+
counts[entry.capabilityId] = (counts[entry.capabilityId] ?? 0) + 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return Object.entries(counts)
|
|
138
|
+
.sort(([, a], [, b]) => b - a)
|
|
139
|
+
.slice(0, limit)
|
|
140
|
+
.map(([id, hits]) => ({ id, hits }));
|
|
141
|
+
}
|
|
142
|
+
async clear() {
|
|
143
|
+
this.entries = [];
|
|
144
|
+
}
|
|
145
|
+
}
|