la-machina-engine 0.19.7 → 0.20.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/dist/index.cjs +199 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +199 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9078,6 +9078,123 @@ function describe(svc) {
|
|
|
9078
9078
|
endpoints
|
|
9079
9079
|
};
|
|
9080
9080
|
}
|
|
9081
|
+
function describeEndpoint(ep) {
|
|
9082
|
+
return {
|
|
9083
|
+
method: ep.method,
|
|
9084
|
+
path: ep.path,
|
|
9085
|
+
description: ep.description,
|
|
9086
|
+
...ep.inputSchema !== void 0 ? { inputSchema: ep.inputSchema } : {},
|
|
9087
|
+
...ep.outputHint !== void 0 ? { outputHint: ep.outputHint } : {},
|
|
9088
|
+
...ep.pagination !== void 0 ? { pagination: ep.pagination } : {},
|
|
9089
|
+
...ep.response !== void 0 ? { response: ep.response } : {}
|
|
9090
|
+
};
|
|
9091
|
+
}
|
|
9092
|
+
var DEFAULT_SEARCH_LIMIT = 8;
|
|
9093
|
+
var MAX_SEARCH_LIMIT = 20;
|
|
9094
|
+
var FIELD_WEIGHTS = {
|
|
9095
|
+
path: 3,
|
|
9096
|
+
method: 2,
|
|
9097
|
+
description: 2,
|
|
9098
|
+
inputKey: 1,
|
|
9099
|
+
outputHint: 1,
|
|
9100
|
+
response: 1
|
|
9101
|
+
};
|
|
9102
|
+
function tokenize(input) {
|
|
9103
|
+
return input.toLowerCase().split(/[\s/_-]+/).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
9104
|
+
}
|
|
9105
|
+
function isObject(v) {
|
|
9106
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
9107
|
+
}
|
|
9108
|
+
function scoreEndpointMatch(ep, tokens, opts) {
|
|
9109
|
+
let score = 0;
|
|
9110
|
+
const matched = /* @__PURE__ */ new Set();
|
|
9111
|
+
const pathLower = ep.path.toLowerCase();
|
|
9112
|
+
const methodLower = ep.method.toLowerCase();
|
|
9113
|
+
const descriptionLower = ep.description.toLowerCase();
|
|
9114
|
+
const outputHintLower = ep.outputHint?.toLowerCase() ?? "";
|
|
9115
|
+
const responseBlob = [ep.response?.itemsPath, ep.response?.totalPath, ep.pagination?.mode].filter((s) => typeof s === "string" && s.length > 0).join(" ").toLowerCase();
|
|
9116
|
+
const inputKeys = [];
|
|
9117
|
+
if (isObject(ep.inputSchema)) {
|
|
9118
|
+
const props = ep.inputSchema.properties;
|
|
9119
|
+
if (isObject(props)) {
|
|
9120
|
+
for (const key of Object.keys(props)) inputKeys.push(key);
|
|
9121
|
+
}
|
|
9122
|
+
}
|
|
9123
|
+
const inputKeysLower = inputKeys.map((k) => k.toLowerCase());
|
|
9124
|
+
for (const t of tokens) {
|
|
9125
|
+
if (pathLower.includes(t)) {
|
|
9126
|
+
score += FIELD_WEIGHTS.path;
|
|
9127
|
+
matched.add("path");
|
|
9128
|
+
}
|
|
9129
|
+
if (opts.pathOnly) continue;
|
|
9130
|
+
if (t === methodLower) {
|
|
9131
|
+
score += FIELD_WEIGHTS.method;
|
|
9132
|
+
matched.add("method");
|
|
9133
|
+
}
|
|
9134
|
+
if (descriptionLower.includes(t)) {
|
|
9135
|
+
score += FIELD_WEIGHTS.description;
|
|
9136
|
+
matched.add("description");
|
|
9137
|
+
}
|
|
9138
|
+
if (inputKeysLower.some((k) => k.includes(t))) {
|
|
9139
|
+
score += FIELD_WEIGHTS.inputKey;
|
|
9140
|
+
matched.add("inputKey");
|
|
9141
|
+
}
|
|
9142
|
+
if (outputHintLower.length > 0 && outputHintLower.includes(t)) {
|
|
9143
|
+
score += FIELD_WEIGHTS.outputHint;
|
|
9144
|
+
matched.add("outputHint");
|
|
9145
|
+
}
|
|
9146
|
+
if (responseBlob.length > 0 && responseBlob.includes(t)) {
|
|
9147
|
+
score += FIELD_WEIGHTS.response;
|
|
9148
|
+
matched.add("response");
|
|
9149
|
+
}
|
|
9150
|
+
}
|
|
9151
|
+
if (score === 0) return { score: 0, matched: [] };
|
|
9152
|
+
return {
|
|
9153
|
+
score,
|
|
9154
|
+
matched: [...matched].sort(),
|
|
9155
|
+
...inputKeys.length > 0 ? { inputKeys } : {}
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
function searchService(svc, opts) {
|
|
9159
|
+
const tokens = tokenize(opts.query);
|
|
9160
|
+
const endpoints = svc.endpoints ?? [];
|
|
9161
|
+
const scored = [];
|
|
9162
|
+
for (const ep of endpoints) {
|
|
9163
|
+
if (opts.method !== void 0 && ep.method !== opts.method) continue;
|
|
9164
|
+
const r = scoreEndpointMatch(ep, tokens, { pathOnly: opts.pathOnly });
|
|
9165
|
+
if (r.score === 0) continue;
|
|
9166
|
+
scored.push({
|
|
9167
|
+
ep,
|
|
9168
|
+
score: r.score,
|
|
9169
|
+
matched: r.matched,
|
|
9170
|
+
...r.inputKeys !== void 0 ? { inputKeys: r.inputKeys } : {}
|
|
9171
|
+
});
|
|
9172
|
+
}
|
|
9173
|
+
scored.sort((a, b) => {
|
|
9174
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
9175
|
+
if (a.ep.method !== b.ep.method) {
|
|
9176
|
+
return a.ep.method < b.ep.method ? -1 : 1;
|
|
9177
|
+
}
|
|
9178
|
+
if (a.ep.path !== b.ep.path) return a.ep.path < b.ep.path ? -1 : 1;
|
|
9179
|
+
return 0;
|
|
9180
|
+
});
|
|
9181
|
+
const truncated = scored.length > opts.limit;
|
|
9182
|
+
const slice = scored.slice(0, opts.limit);
|
|
9183
|
+
return {
|
|
9184
|
+
service: svc.name,
|
|
9185
|
+
query: opts.query,
|
|
9186
|
+
matches: slice.map((s) => ({
|
|
9187
|
+
method: s.ep.method,
|
|
9188
|
+
path: s.ep.path,
|
|
9189
|
+
description: s.ep.description,
|
|
9190
|
+
score: s.score,
|
|
9191
|
+
matched: s.matched,
|
|
9192
|
+
...s.inputKeys !== void 0 && s.inputKeys.length > 0 ? { inputKeys: s.inputKeys } : {},
|
|
9193
|
+
...s.ep.outputHint !== void 0 ? { outputHint: s.ep.outputHint } : {}
|
|
9194
|
+
})),
|
|
9195
|
+
...truncated ? { truncated: true } : {}
|
|
9196
|
+
};
|
|
9197
|
+
}
|
|
9081
9198
|
function hasDescribableEndpoints(svc) {
|
|
9082
9199
|
return svc.endpoints !== void 0 && svc.endpoints.length > 0;
|
|
9083
9200
|
}
|
|
@@ -9093,18 +9210,39 @@ function createDescribeServiceTool(opts) {
|
|
|
9093
9210
|
const serviceNames = [...serviceMap.keys()];
|
|
9094
9211
|
const cache = /* @__PURE__ */ new Map();
|
|
9095
9212
|
const inputSchema19 = import_zod24.z.object({
|
|
9096
|
-
service: import_zod24.z.enum(serviceNames)
|
|
9213
|
+
service: import_zod24.z.enum(serviceNames),
|
|
9214
|
+
/**
|
|
9215
|
+
* Plan 054 — free-text query. When set with no `path`, returns
|
|
9216
|
+
* the compact ranked-match shape. Must be a non-empty string.
|
|
9217
|
+
*/
|
|
9218
|
+
query: import_zod24.z.string().min(1).optional(),
|
|
9219
|
+
/**
|
|
9220
|
+
* Plan 054 — narrow ranked matches to one HTTP verb. Also used
|
|
9221
|
+
* by the exact-endpoint branch in combination with `path`.
|
|
9222
|
+
*/
|
|
9223
|
+
method: import_zod24.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).optional(),
|
|
9224
|
+
/**
|
|
9225
|
+
* Plan 054 — paired with `method` returns full endpoint detail;
|
|
9226
|
+
* paired without `method` runs a path-only ranked search.
|
|
9227
|
+
*/
|
|
9228
|
+
path: import_zod24.z.string().min(1).optional(),
|
|
9229
|
+
/**
|
|
9230
|
+
* Plan 054 — defaults to 8, capped at 20. Only meaningful in
|
|
9231
|
+
* search modes; ignored when fetching the full catalog or one
|
|
9232
|
+
* exact endpoint.
|
|
9233
|
+
*/
|
|
9234
|
+
limit: import_zod24.z.number().int().min(1).max(MAX_SEARCH_LIMIT).optional()
|
|
9097
9235
|
});
|
|
9098
|
-
const description = `Look up
|
|
9236
|
+
const description = `Look up endpoints on one configured API service.
|
|
9237
|
+
1. Search: DescribeService({ service, query }) returns compact ranked matches with method, path, description, and a relevance score. Use this for broad services where only one endpoint slice is needed. Add { method } to filter.
|
|
9238
|
+
2. Detail: DescribeService({ service, method, path }) returns full endpoint detail including inputSchema. Call this after a search has picked a candidate.
|
|
9239
|
+
3. Execute: ApiCall(...) with the chosen method, path, and inputs.
|
|
9240
|
+
Legacy: DescribeService({ service }) returns the full endpoint catalog \u2014 expensive for large services and discouraged for new code. Services: ${serviceNames.join(", ")}.`;
|
|
9099
9241
|
return defineTool({
|
|
9100
9242
|
name: opts.toolName ?? "DescribeService",
|
|
9101
9243
|
description,
|
|
9102
9244
|
inputSchema: inputSchema19,
|
|
9103
9245
|
execute: async (input) => {
|
|
9104
|
-
const cached = cache.get(input.service);
|
|
9105
|
-
if (cached !== void 0) {
|
|
9106
|
-
return { content: JSON.stringify(cached), metadata: { cached: true } };
|
|
9107
|
-
}
|
|
9108
9246
|
const svc = serviceMap.get(input.service);
|
|
9109
9247
|
if (!svc) {
|
|
9110
9248
|
return {
|
|
@@ -9112,6 +9250,56 @@ function createDescribeServiceTool(opts) {
|
|
|
9112
9250
|
isError: true
|
|
9113
9251
|
};
|
|
9114
9252
|
}
|
|
9253
|
+
const limit = Math.min(input.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
|
|
9254
|
+
if (input.method !== void 0 && input.path !== void 0) {
|
|
9255
|
+
const ep = (svc.endpoints ?? []).find(
|
|
9256
|
+
(e) => e.method === input.method && e.path === input.path
|
|
9257
|
+
);
|
|
9258
|
+
if (ep === void 0) {
|
|
9259
|
+
return {
|
|
9260
|
+
content: JSON.stringify({
|
|
9261
|
+
code: "ENDPOINT_NOT_FOUND",
|
|
9262
|
+
service: input.service,
|
|
9263
|
+
method: input.method,
|
|
9264
|
+
path: input.path,
|
|
9265
|
+
message: `No endpoint ${input.method} ${input.path} on service "${input.service}". Call DescribeService with { service, query } to search the catalog.`
|
|
9266
|
+
}),
|
|
9267
|
+
isError: true
|
|
9268
|
+
};
|
|
9269
|
+
}
|
|
9270
|
+
return {
|
|
9271
|
+
content: JSON.stringify(describeEndpoint(ep)),
|
|
9272
|
+
metadata: { mode: "detail" }
|
|
9273
|
+
};
|
|
9274
|
+
}
|
|
9275
|
+
if (input.query !== void 0) {
|
|
9276
|
+
const result = searchService(svc, {
|
|
9277
|
+
query: input.query,
|
|
9278
|
+
...input.method !== void 0 ? { method: input.method } : {},
|
|
9279
|
+
limit,
|
|
9280
|
+
pathOnly: false
|
|
9281
|
+
});
|
|
9282
|
+
return {
|
|
9283
|
+
content: JSON.stringify(result),
|
|
9284
|
+
metadata: { mode: "search" }
|
|
9285
|
+
};
|
|
9286
|
+
}
|
|
9287
|
+
if (input.path !== void 0) {
|
|
9288
|
+
const result = searchService(svc, {
|
|
9289
|
+
query: input.path,
|
|
9290
|
+
...input.method !== void 0 ? { method: input.method } : {},
|
|
9291
|
+
limit,
|
|
9292
|
+
pathOnly: true
|
|
9293
|
+
});
|
|
9294
|
+
return {
|
|
9295
|
+
content: JSON.stringify(result),
|
|
9296
|
+
metadata: { mode: "search" }
|
|
9297
|
+
};
|
|
9298
|
+
}
|
|
9299
|
+
const cached = cache.get(input.service);
|
|
9300
|
+
if (cached !== void 0) {
|
|
9301
|
+
return { content: JSON.stringify(cached), metadata: { cached: true } };
|
|
9302
|
+
}
|
|
9115
9303
|
const payload = describe(svc);
|
|
9116
9304
|
cache.set(input.service, payload);
|
|
9117
9305
|
return { content: JSON.stringify(payload), metadata: { cached: false } };
|
|
@@ -9188,7 +9376,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
9188
9376
|
"he",
|
|
9189
9377
|
"she"
|
|
9190
9378
|
]);
|
|
9191
|
-
function
|
|
9379
|
+
function tokenize2(text2) {
|
|
9192
9380
|
if (typeof text2 !== "string" || text2.length === 0) return [];
|
|
9193
9381
|
const seen = /* @__PURE__ */ new Set();
|
|
9194
9382
|
for (const raw of text2.toLowerCase().split(/[\W_]+/)) {
|
|
@@ -9303,7 +9491,7 @@ function splitSections(content, relPath) {
|
|
|
9303
9491
|
heading: "",
|
|
9304
9492
|
slug: `${relPath}#`,
|
|
9305
9493
|
depth: 0,
|
|
9306
|
-
words:
|
|
9494
|
+
words: tokenize2(leadInBody),
|
|
9307
9495
|
preview: makePreview(leadInBody),
|
|
9308
9496
|
startLine: 1,
|
|
9309
9497
|
endLine: leadInEndLine
|
|
@@ -9319,7 +9507,7 @@ function splitSections(content, relPath) {
|
|
|
9319
9507
|
heading: h.heading,
|
|
9320
9508
|
slug: `${relPath}#${slugify(h.heading)}`,
|
|
9321
9509
|
depth: h.depth,
|
|
9322
|
-
words:
|
|
9510
|
+
words: tokenize2(body),
|
|
9323
9511
|
preview: makePreview(body),
|
|
9324
9512
|
startLine,
|
|
9325
9513
|
endLine
|
|
@@ -9436,7 +9624,7 @@ function createSearchKnowledgeTool(opts) {
|
|
|
9436
9624
|
inputSchema: inputSchema17,
|
|
9437
9625
|
execute: async ({ query, maxResults }) => {
|
|
9438
9626
|
const limit = Math.min(maxResults ?? cap, cap);
|
|
9439
|
-
const queryTokens =
|
|
9627
|
+
const queryTokens = tokenize2(query);
|
|
9440
9628
|
if (queryTokens.length === 0) {
|
|
9441
9629
|
return { content: "no searchable tokens in query", isError: false };
|
|
9442
9630
|
}
|
|
@@ -9461,7 +9649,7 @@ function createSearchKnowledgeTool(opts) {
|
|
|
9461
9649
|
}
|
|
9462
9650
|
const externalHits = [];
|
|
9463
9651
|
for (const link of externals) {
|
|
9464
|
-
const score = scoreOverlap(
|
|
9652
|
+
const score = scoreOverlap(tokenize2(link.description), queryTokens);
|
|
9465
9653
|
if (score > 0) externalHits.push({ link, score });
|
|
9466
9654
|
}
|
|
9467
9655
|
const all = [
|