@f5-sales-demo/xcsh 22.1.2 → 22.2.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/package.json +8 -8
- package/src/internal-urls/api-catalog-discovery.ts +56 -1
- package/src/internal-urls/api-catalog-preflight.ts +35 -63
- package/src/internal-urls/api-catalog-qmd-index.generated.ts +2 -2
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/knowledge-classifier.ts +209 -0
- package/src/internal-urls/knowledge-search.ts +90 -0
- package/src/prompts/internal-urls/about.md +1 -1
- package/src/prompts/rules/llms-search.md +10 -1
- package/src/prompts/system/system-prompt.md +15 -1
- package/src/prompts/tools/display-media.md +4 -0
- package/src/services/xcsh-knowledge.ts +6 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "22.
|
|
4
|
+
"version": "22.2.0",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
70
|
-
"@f5-sales-demo/pi-agent-core": "22.
|
|
71
|
-
"@f5-sales-demo/pi-ai": "22.
|
|
72
|
-
"@f5-sales-demo/pi-natives": "22.
|
|
73
|
-
"@f5-sales-demo/pi-resource-management": "22.
|
|
74
|
-
"@f5-sales-demo/pi-tui": "22.
|
|
75
|
-
"@f5-sales-demo/pi-utils": "22.
|
|
76
|
-
"@f5-sales-demo/xcsh-stats": "22.
|
|
70
|
+
"@f5-sales-demo/pi-agent-core": "22.2.0",
|
|
71
|
+
"@f5-sales-demo/pi-ai": "22.2.0",
|
|
72
|
+
"@f5-sales-demo/pi-natives": "22.2.0",
|
|
73
|
+
"@f5-sales-demo/pi-resource-management": "22.2.0",
|
|
74
|
+
"@f5-sales-demo/pi-tui": "22.2.0",
|
|
75
|
+
"@f5-sales-demo/pi-utils": "22.2.0",
|
|
76
|
+
"@f5-sales-demo/xcsh-stats": "22.2.0",
|
|
77
77
|
"@mozilla/readability": "^0.6",
|
|
78
78
|
"@resvg/resvg-wasm": "2.6.2",
|
|
79
79
|
"@sinclair/typebox": "0.34.52",
|
|
@@ -95,6 +95,48 @@ export function matchesBaselineCatalogDiscoveryTerm(term: string, values: readon
|
|
|
95
95
|
return values.some(value => normalizeApiCatalogDiscoveryTerm(value).includes(normalized));
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function categoryDocuments(category: ApiCatalogCategory): ApiCatalogDiscoveryDocument[] {
|
|
99
|
+
const destination = `xcsh://api-catalog/${category.name}`;
|
|
100
|
+
if (category.operations.length === 0) {
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
id: `category:${category.name}`,
|
|
104
|
+
categoryName: category.name,
|
|
105
|
+
markdown: [
|
|
106
|
+
`# ${category.displayName}`,
|
|
107
|
+
"",
|
|
108
|
+
`- Category: ${category.name}`,
|
|
109
|
+
`- Destination: ${destination}`,
|
|
110
|
+
"",
|
|
111
|
+
"## Operations",
|
|
112
|
+
"- None",
|
|
113
|
+
"",
|
|
114
|
+
].join("\n"),
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
return category.operations
|
|
119
|
+
.slice()
|
|
120
|
+
.sort((left, right) => left.operationId.localeCompare(right.operationId))
|
|
121
|
+
.map(operation => ({
|
|
122
|
+
id: `operation:${category.name}:${operation.operationId}`,
|
|
123
|
+
categoryName: category.name,
|
|
124
|
+
markdown: [
|
|
125
|
+
`# ${category.displayName}: ${operation.name}`,
|
|
126
|
+
"",
|
|
127
|
+
`- Category: ${category.name}`,
|
|
128
|
+
`- Destination: ${destination}`,
|
|
129
|
+
`- Operation: ${operation.name}`,
|
|
130
|
+
`- Alias: ${(operation.operationAliases ?? []).join(", ")}`,
|
|
131
|
+
`- Description: ${operation.description}`,
|
|
132
|
+
`- Method: ${operation.method.toUpperCase()}`,
|
|
133
|
+
`- Path: ${operation.path}`,
|
|
134
|
+
`- Operation ID: ${operation.operationId}`,
|
|
135
|
+
"",
|
|
136
|
+
].join("\n"),
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
|
|
98
140
|
function categoryDocument(category: ApiCatalogCategory): ApiCatalogDiscoveryDocument {
|
|
99
141
|
const destination = `xcsh://api-catalog/${category.name}`;
|
|
100
142
|
const operations = category.operations.flatMap(operation => [
|
|
@@ -105,7 +147,6 @@ function categoryDocument(category: ApiCatalogCategory): ApiCatalogDiscoveryDocu
|
|
|
105
147
|
` - Path: ${operation.path}`,
|
|
106
148
|
` - Operation ID: ${operation.operationId}`,
|
|
107
149
|
]);
|
|
108
|
-
|
|
109
150
|
return {
|
|
110
151
|
id: `category:${category.name}`,
|
|
111
152
|
categoryName: category.name,
|
|
@@ -139,6 +180,20 @@ export function buildApiCatalogDiscoveryCorpus(
|
|
|
139
180
|
};
|
|
140
181
|
}
|
|
141
182
|
|
|
183
|
+
/** Candidate-only operation granularity; authoritative results remain category URLs. */
|
|
184
|
+
export function buildApiOperationDiscoveryCorpus(
|
|
185
|
+
index: ApiCatalogIndex,
|
|
186
|
+
data: Readonly<Record<string, ApiCatalogCategory>>,
|
|
187
|
+
): ApiCatalogDiscoveryCorpus {
|
|
188
|
+
return {
|
|
189
|
+
catalogVersion: index.version,
|
|
190
|
+
documents: Object.values(data)
|
|
191
|
+
.slice()
|
|
192
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
193
|
+
.flatMap(categoryDocuments),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
142
197
|
export function fingerprintApiCatalogDiscoveryCorpus(
|
|
143
198
|
corpus: ApiCatalogDiscoveryCorpus,
|
|
144
199
|
sourceSha: string,
|
|
@@ -3,18 +3,20 @@ import path from "node:path";
|
|
|
3
3
|
import { rankQmdBm25CatalogDiscovery } from "./api-catalog-discovery";
|
|
4
4
|
import { QMD_API_CATALOG_PREBUILT_INDEX } from "./api-catalog-qmd-index.generated";
|
|
5
5
|
import type { ApiSpecIndex } from "./api-spec-types";
|
|
6
|
+
import {
|
|
7
|
+
buildKnowledgeSearchPlan,
|
|
8
|
+
classifyKnowledgeRequest,
|
|
9
|
+
type KnowledgeClassifierResource,
|
|
10
|
+
type KnowledgeSearchPlan,
|
|
11
|
+
} from "./knowledge-classifier";
|
|
6
12
|
|
|
7
|
-
export interface ApiCatalogPreflightResource {
|
|
8
|
-
readonly name: string;
|
|
9
|
-
readonly aliases: readonly string[];
|
|
10
|
-
readonly domain: string;
|
|
11
|
-
readonly categories: readonly string[];
|
|
12
|
-
}
|
|
13
|
+
export interface ApiCatalogPreflightResource extends KnowledgeClassifierResource {}
|
|
13
14
|
|
|
14
15
|
export interface ApiCatalogPreflightIntent {
|
|
15
16
|
readonly resource: string;
|
|
16
17
|
readonly domain: string;
|
|
17
18
|
readonly queries: readonly string[];
|
|
19
|
+
readonly structuredQuery?: KnowledgeSearchPlan;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface ApiCatalogPreflightDestination {
|
|
@@ -45,16 +47,6 @@ interface RunApiCatalogPreflightOptions extends ApiCatalogPreflightOptions {
|
|
|
45
47
|
readonly rank?: (query: string) => Promise<readonly string[]>;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
const EXCLUDED_INTENT =
|
|
49
|
-
/\b(?:troubleshoot|debug|not working|returning\s+\d{3}|pricing|price|quote|licen[cs]ing|sales)\b/i;
|
|
50
|
-
const THIRD_PARTY_SCOPE = /\b(?:aws|amazon|azure|gcp|google cloud|kubernetes)\b/i;
|
|
51
|
-
const F5_XC_SCOPE = /\b(?:f5(?:\s+distributed\s+cloud)?|xc)\b/i;
|
|
52
|
-
const ANAPHORIC_RESOURCE = /\b(?:its|it|that|this)\b/i;
|
|
53
|
-
const DOCUMENTATION_ROUTE = /xcsh:\/\/documentation(?:\/|\?)/i;
|
|
54
|
-
const API_METADATA_INTENT =
|
|
55
|
-
/\b(?:endpoint|api\s+path|http\s+method|method|payload|request\s+body|required\s+fields?|enum|allowed\s+values?|constraints?|limits?|maximum|max(?:imum)?|minimum|min(?:imum)?|how\s+many|create|creates|creating|get|gets|list|lists|update|updates|replace|replaces|delete|deletes|clone|import)\b/i;
|
|
56
|
-
const EXPLICIT_API_INTENT = /\b(?:api|endpoint|method|payload|schema)\b/i;
|
|
57
|
-
|
|
58
50
|
let generatedResources: readonly ApiCatalogPreflightResource[] | undefined;
|
|
59
51
|
let generatedCatalogVersion: string | undefined;
|
|
60
52
|
|
|
@@ -67,10 +59,6 @@ function normalizedPhrase(value: string): string {
|
|
|
67
59
|
.replace(/\s+/g, " ");
|
|
68
60
|
}
|
|
69
61
|
|
|
70
|
-
function preferredAlias(resource: { name: string; aliases: readonly string[] }): string {
|
|
71
|
-
return normalizedPhrase(resource.aliases[0] ?? resource.name);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
62
|
function defaultAliases(name: string, descriptionShort?: string): string[] {
|
|
75
63
|
const canonical = normalizedPhrase(name);
|
|
76
64
|
const aliases = [normalizedPhrase(descriptionShort ?? ""), canonical].filter(Boolean);
|
|
@@ -97,56 +85,40 @@ function loadGeneratedMetadata(): { resources: readonly ApiCatalogPreflightResou
|
|
|
97
85
|
return { resources: generatedResources, catalogVersion: generatedCatalogVersion };
|
|
98
86
|
}
|
|
99
87
|
|
|
100
|
-
function crudVerb(prompt: string): string | undefined {
|
|
101
|
-
const value = normalizedPhrase(prompt);
|
|
102
|
-
if (/\b(?:create|creates|creating)\b/.test(value)) return "create";
|
|
103
|
-
if (/\b(?:get|gets|read|fetch)\b/.test(value)) return "get";
|
|
104
|
-
if (/\b(?:list|lists)\b/.test(value)) return "list";
|
|
105
|
-
if (/\b(?:update|updates)\b/.test(value)) return "update";
|
|
106
|
-
if (/\b(?:replace|replaces)\b/.test(value)) return "replace";
|
|
107
|
-
if (/\b(?:delete|deletes|remove)\b/.test(value)) return "delete";
|
|
108
|
-
if (/\bclone\b/.test(value)) return "clone";
|
|
109
|
-
if (/\bimport\b/.test(value)) return "import";
|
|
110
|
-
return undefined;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
88
|
export function classifyApiCatalogPreflight(
|
|
114
89
|
prompt: string,
|
|
115
90
|
options: ApiCatalogPreflightOptions,
|
|
116
91
|
): ApiCatalogPreflightIntent | null {
|
|
117
|
-
if (!options.toolsEnabled || DOCUMENTATION_ROUTE.test(prompt) || EXCLUDED_INTENT.test(prompt)) return null;
|
|
118
|
-
if (THIRD_PARTY_SCOPE.test(prompt) && !F5_XC_SCOPE.test(prompt)) return null;
|
|
119
|
-
if (/\bterraform\b/i.test(prompt) && !EXPLICIT_API_INTENT.test(prompt)) return null;
|
|
120
|
-
if (!API_METADATA_INTENT.test(prompt)) return null;
|
|
121
|
-
|
|
122
92
|
const resources = options.resources ?? loadGeneratedMetadata().resources;
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
140
|
-
|
|
141
|
-
|
|
93
|
+
const previous = options.previousResource
|
|
94
|
+
? {
|
|
95
|
+
route: "api" as const,
|
|
96
|
+
confidence: 0.99,
|
|
97
|
+
resource: options.previousResource.resource,
|
|
98
|
+
domain: options.previousResource.domain,
|
|
99
|
+
constraints: { domain: options.previousResource.domain },
|
|
100
|
+
query:
|
|
101
|
+
options.previousResource.structuredQuery ??
|
|
102
|
+
buildKnowledgeSearchPlan(
|
|
103
|
+
"api",
|
|
104
|
+
options.previousResource.queries[0] ?? options.previousResource.resource,
|
|
105
|
+
),
|
|
106
|
+
}
|
|
107
|
+
: undefined;
|
|
108
|
+
const classified = classifyKnowledgeRequest(prompt, { toolsEnabled: options.toolsEnabled, resources, previous });
|
|
109
|
+
if (classified.route !== "api" || !classified.resource || !classified.domain) return null;
|
|
110
|
+
if (
|
|
111
|
+
options.previousResource &&
|
|
112
|
+
classified.resource === options.previousResource.resource &&
|
|
113
|
+
/\b(?:its|it|that|this|those|them|the same)\b/i.test(prompt)
|
|
114
|
+
) {
|
|
115
|
+
return options.previousResource;
|
|
142
116
|
}
|
|
143
|
-
|
|
144
|
-
const alias = preferredAlias(selected);
|
|
145
|
-
const verb = crudVerb(prompt);
|
|
146
117
|
return {
|
|
147
|
-
resource:
|
|
148
|
-
domain:
|
|
149
|
-
queries:
|
|
118
|
+
resource: classified.resource,
|
|
119
|
+
domain: classified.domain,
|
|
120
|
+
queries: classified.query.lex,
|
|
121
|
+
structuredQuery: classified.query,
|
|
150
122
|
};
|
|
151
123
|
}
|
|
152
124
|
|