la-machina-engine 0.19.7 → 0.20.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/dist/index.cjs +204 -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 +204 -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,44 @@ 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 MAX_SEARCH_LIMIT.
|
|
9231
|
+
* Plan 054 review fix LOW — Zod no longer hard-rejects
|
|
9232
|
+
* values above the cap. A model that picks `limit: 100`
|
|
9233
|
+
* shouldn't waste a turn on a validation error; the
|
|
9234
|
+
* runtime `Math.min` clamp below is the authority and
|
|
9235
|
+
* silently bounds to 20. Schema still rejects non-int /
|
|
9236
|
+
* non-positive values so we don't silently round a sloppy
|
|
9237
|
+
* float or treat negatives as the default.
|
|
9238
|
+
*/
|
|
9239
|
+
limit: import_zod24.z.number().int().min(1).optional()
|
|
9097
9240
|
});
|
|
9098
|
-
const description = `Look up
|
|
9241
|
+
const description = `Look up endpoints on one configured API service.
|
|
9242
|
+
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.
|
|
9243
|
+
2. Detail: DescribeService({ service, method, path }) returns full endpoint detail including inputSchema. Call this after a search has picked a candidate.
|
|
9244
|
+
3. Execute: ApiCall(...) with the chosen method, path, and inputs.
|
|
9245
|
+
Legacy: DescribeService({ service }) returns the full endpoint catalog \u2014 expensive for large services and discouraged for new code. Services: ${serviceNames.join(", ")}.`;
|
|
9099
9246
|
return defineTool({
|
|
9100
9247
|
name: opts.toolName ?? "DescribeService",
|
|
9101
9248
|
description,
|
|
9102
9249
|
inputSchema: inputSchema19,
|
|
9103
9250
|
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
9251
|
const svc = serviceMap.get(input.service);
|
|
9109
9252
|
if (!svc) {
|
|
9110
9253
|
return {
|
|
@@ -9112,6 +9255,56 @@ function createDescribeServiceTool(opts) {
|
|
|
9112
9255
|
isError: true
|
|
9113
9256
|
};
|
|
9114
9257
|
}
|
|
9258
|
+
const limit = Math.min(input.limit ?? DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT);
|
|
9259
|
+
if (input.method !== void 0 && input.path !== void 0) {
|
|
9260
|
+
const ep = (svc.endpoints ?? []).find(
|
|
9261
|
+
(e) => e.method === input.method && e.path === input.path
|
|
9262
|
+
);
|
|
9263
|
+
if (ep === void 0) {
|
|
9264
|
+
return {
|
|
9265
|
+
content: JSON.stringify({
|
|
9266
|
+
code: "ENDPOINT_NOT_FOUND",
|
|
9267
|
+
service: input.service,
|
|
9268
|
+
method: input.method,
|
|
9269
|
+
path: input.path,
|
|
9270
|
+
message: `No endpoint ${input.method} ${input.path} on service "${input.service}". Call DescribeService with { service, query } to search the catalog.`
|
|
9271
|
+
}),
|
|
9272
|
+
isError: true
|
|
9273
|
+
};
|
|
9274
|
+
}
|
|
9275
|
+
return {
|
|
9276
|
+
content: JSON.stringify(describeEndpoint(ep)),
|
|
9277
|
+
metadata: { mode: "detail" }
|
|
9278
|
+
};
|
|
9279
|
+
}
|
|
9280
|
+
if (input.query !== void 0) {
|
|
9281
|
+
const result = searchService(svc, {
|
|
9282
|
+
query: input.query,
|
|
9283
|
+
...input.method !== void 0 ? { method: input.method } : {},
|
|
9284
|
+
limit,
|
|
9285
|
+
pathOnly: false
|
|
9286
|
+
});
|
|
9287
|
+
return {
|
|
9288
|
+
content: JSON.stringify(result),
|
|
9289
|
+
metadata: { mode: "search" }
|
|
9290
|
+
};
|
|
9291
|
+
}
|
|
9292
|
+
if (input.path !== void 0) {
|
|
9293
|
+
const result = searchService(svc, {
|
|
9294
|
+
query: input.path,
|
|
9295
|
+
...input.method !== void 0 ? { method: input.method } : {},
|
|
9296
|
+
limit,
|
|
9297
|
+
pathOnly: true
|
|
9298
|
+
});
|
|
9299
|
+
return {
|
|
9300
|
+
content: JSON.stringify(result),
|
|
9301
|
+
metadata: { mode: "search" }
|
|
9302
|
+
};
|
|
9303
|
+
}
|
|
9304
|
+
const cached = cache.get(input.service);
|
|
9305
|
+
if (cached !== void 0) {
|
|
9306
|
+
return { content: JSON.stringify(cached), metadata: { cached: true } };
|
|
9307
|
+
}
|
|
9115
9308
|
const payload = describe(svc);
|
|
9116
9309
|
cache.set(input.service, payload);
|
|
9117
9310
|
return { content: JSON.stringify(payload), metadata: { cached: false } };
|
|
@@ -9188,7 +9381,7 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
9188
9381
|
"he",
|
|
9189
9382
|
"she"
|
|
9190
9383
|
]);
|
|
9191
|
-
function
|
|
9384
|
+
function tokenize2(text2) {
|
|
9192
9385
|
if (typeof text2 !== "string" || text2.length === 0) return [];
|
|
9193
9386
|
const seen = /* @__PURE__ */ new Set();
|
|
9194
9387
|
for (const raw of text2.toLowerCase().split(/[\W_]+/)) {
|
|
@@ -9303,7 +9496,7 @@ function splitSections(content, relPath) {
|
|
|
9303
9496
|
heading: "",
|
|
9304
9497
|
slug: `${relPath}#`,
|
|
9305
9498
|
depth: 0,
|
|
9306
|
-
words:
|
|
9499
|
+
words: tokenize2(leadInBody),
|
|
9307
9500
|
preview: makePreview(leadInBody),
|
|
9308
9501
|
startLine: 1,
|
|
9309
9502
|
endLine: leadInEndLine
|
|
@@ -9319,7 +9512,7 @@ function splitSections(content, relPath) {
|
|
|
9319
9512
|
heading: h.heading,
|
|
9320
9513
|
slug: `${relPath}#${slugify(h.heading)}`,
|
|
9321
9514
|
depth: h.depth,
|
|
9322
|
-
words:
|
|
9515
|
+
words: tokenize2(body),
|
|
9323
9516
|
preview: makePreview(body),
|
|
9324
9517
|
startLine,
|
|
9325
9518
|
endLine
|
|
@@ -9436,7 +9629,7 @@ function createSearchKnowledgeTool(opts) {
|
|
|
9436
9629
|
inputSchema: inputSchema17,
|
|
9437
9630
|
execute: async ({ query, maxResults }) => {
|
|
9438
9631
|
const limit = Math.min(maxResults ?? cap, cap);
|
|
9439
|
-
const queryTokens =
|
|
9632
|
+
const queryTokens = tokenize2(query);
|
|
9440
9633
|
if (queryTokens.length === 0) {
|
|
9441
9634
|
return { content: "no searchable tokens in query", isError: false };
|
|
9442
9635
|
}
|
|
@@ -9461,7 +9654,7 @@ function createSearchKnowledgeTool(opts) {
|
|
|
9461
9654
|
}
|
|
9462
9655
|
const externalHits = [];
|
|
9463
9656
|
for (const link of externals) {
|
|
9464
|
-
const score = scoreOverlap(
|
|
9657
|
+
const score = scoreOverlap(tokenize2(link.description), queryTokens);
|
|
9465
9658
|
if (score > 0) externalHits.push({ link, score });
|
|
9466
9659
|
}
|
|
9467
9660
|
const all = [
|