@bpmsoftwaresolutions/ai-engine-client 0.1.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/README.md +69 -0
- package/package.json +28 -0
- package/src/index.js +162 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @bpmsoftwaresolutions/ai-engine-client
|
|
2
|
+
|
|
3
|
+
Thin npm client for AI Engine's supported operator and retrieval APIs.
|
|
4
|
+
|
|
5
|
+
## Why this package exists
|
|
6
|
+
|
|
7
|
+
This package is the no-clone distribution shape for coworkers and other consumers.
|
|
8
|
+
|
|
9
|
+
It does not embed the full AI Engine runtime. It talks to a running AI Engine service over its stable HTTP surfaces.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @bpmsoftwaresolutions/ai-engine-client
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { AIEngineClient } from '@bpmsoftwaresolutions/ai-engine-client';
|
|
21
|
+
|
|
22
|
+
const client = new AIEngineClient({
|
|
23
|
+
baseUrl: 'https://resume-generator-api.politehill-3458aba1.eastus.azurecontainerapps.io'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const health = await client.ping();
|
|
27
|
+
const workflowStatus = await client.currentWorkflowStatus();
|
|
28
|
+
const procedure = await client.resolveOperatingProcedure({ intentText: 'startup' });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The Azure-hosted base URL above is the current working no-clone path.
|
|
32
|
+
|
|
33
|
+
## Supported methods
|
|
34
|
+
|
|
35
|
+
- `ping()`
|
|
36
|
+
- `currentWorkflowStatus()`
|
|
37
|
+
- `currentArchitectureIntegrityStatus()`
|
|
38
|
+
- `currentSecurityGovernanceStatus()`
|
|
39
|
+
- `getCommandCard()`
|
|
40
|
+
- `resolveOperatingProcedure()`
|
|
41
|
+
- `getSymbolDefinition()`
|
|
42
|
+
- `getRelatedCode()`
|
|
43
|
+
|
|
44
|
+
## Runtime contract
|
|
45
|
+
|
|
46
|
+
The package expects a running AI Engine web service. The preferred production-style path is the Azure-hosted deployment at:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
https://resume-generator-api.politehill-3458aba1.eastus.azurecontainerapps.io
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You can also point it at the Flask app from this repo or another deployment exposing the same routes.
|
|
53
|
+
|
|
54
|
+
Current required HTTP surfaces:
|
|
55
|
+
|
|
56
|
+
- `/healthz`
|
|
57
|
+
- `/api/operator/current-workflow-status`
|
|
58
|
+
- `/api/operator/current-architecture-integrity-status`
|
|
59
|
+
- `/api/operator/current-security-governance-status`
|
|
60
|
+
- `/api/operator/retrieval/wrapper/command-card`
|
|
61
|
+
- `/api/operator/retrieval/wrapper/operating-procedure`
|
|
62
|
+
- `/api/operator/retrieval/wrapper/symbol-definition`
|
|
63
|
+
- `/api/operator/retrieval/wrapper/related-code`
|
|
64
|
+
|
|
65
|
+
## Distribution note
|
|
66
|
+
|
|
67
|
+
This is the preferred distribution shape when the consumer should not clone the full repo. The full repo is still required only when the consumer is running or developing the backend itself.
|
|
68
|
+
|
|
69
|
+
Known limitation: `getRelatedCode()` is available on the hosted surface, but it currently returns `blocked_insufficient_data` until the relationship index is populated.
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bpmsoftwaresolutions/ai-engine-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Thin npm client for the AI Engine operator and retrieval APIs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ai-engine",
|
|
19
|
+
"sdk",
|
|
20
|
+
"client",
|
|
21
|
+
"retrieval",
|
|
22
|
+
"workflow"
|
|
23
|
+
],
|
|
24
|
+
"license": "UNLICENSED",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
2
|
+
|
|
3
|
+
function trimTrailingSlash(value) {
|
|
4
|
+
return String(value || '').replace(/\/+$/, '');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function appendQuery(url, query) {
|
|
8
|
+
const target = new URL(url);
|
|
9
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
10
|
+
if (value === undefined || value === null || value === '') {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
target.searchParams.set(key, String(value));
|
|
14
|
+
}
|
|
15
|
+
return target;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function readJson(response) {
|
|
19
|
+
const contentType = response.headers.get('content-type') || '';
|
|
20
|
+
if (contentType.includes('application/json')) {
|
|
21
|
+
return response.json();
|
|
22
|
+
}
|
|
23
|
+
const text = await response.text();
|
|
24
|
+
return { message: text };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class AIEngineClient {
|
|
28
|
+
constructor({ baseUrl, apiKey, actorId, fetchImpl, timeoutMs } = {}) {
|
|
29
|
+
if (!baseUrl) {
|
|
30
|
+
throw new Error('baseUrl is required.');
|
|
31
|
+
}
|
|
32
|
+
this.baseUrl = trimTrailingSlash(baseUrl);
|
|
33
|
+
this.apiKey = apiKey || null;
|
|
34
|
+
this.actorId = actorId || 'sdk:npm-ai-engine-client';
|
|
35
|
+
this.fetchImpl = fetchImpl || globalThis.fetch;
|
|
36
|
+
this.timeoutMs = timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
37
|
+
if (typeof this.fetchImpl !== 'function') {
|
|
38
|
+
throw new Error('A fetch implementation is required. Use Node 18+ or supply fetchImpl.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static fromEnv(options = {}) {
|
|
43
|
+
return new AIEngineClient({
|
|
44
|
+
baseUrl: options.baseUrl || process.env.AI_ENGINE_BASE_URL || process.env.AI_ENGINE_API_BASE_URL,
|
|
45
|
+
apiKey: options.apiKey || process.env.AI_ENGINE_API_KEY || null,
|
|
46
|
+
actorId: options.actorId || process.env.AI_ENGINE_ACTOR_ID || 'sdk:npm-ai-engine-client'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async ping() {
|
|
51
|
+
const [health, workflow] = await Promise.all([
|
|
52
|
+
this._request('/healthz'),
|
|
53
|
+
this.currentWorkflowStatus()
|
|
54
|
+
]);
|
|
55
|
+
return {
|
|
56
|
+
status: health.status || 'ok',
|
|
57
|
+
workflow_name: workflow?.summary?.workflow_name || null,
|
|
58
|
+
run_status: workflow?.summary?.run_status || null,
|
|
59
|
+
base_url: this.baseUrl
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async currentWorkflowStatus() {
|
|
64
|
+
return this._request('/api/operator/current-workflow-status');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async currentArchitectureIntegrityStatus() {
|
|
68
|
+
return this._request('/api/operator/current-architecture-integrity-status');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async currentSecurityGovernanceStatus({ environment, topN } = {}) {
|
|
72
|
+
return this._request('/api/operator/current-security-governance-status', {
|
|
73
|
+
query: {
|
|
74
|
+
environment,
|
|
75
|
+
top_n: topN
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getCommandCard({ commandKey, alias, intentText, requestedBy } = {}) {
|
|
81
|
+
return this._request('/api/operator/retrieval/wrapper/command-card', {
|
|
82
|
+
query: {
|
|
83
|
+
command_key: commandKey,
|
|
84
|
+
alias,
|
|
85
|
+
intent_text: intentText,
|
|
86
|
+
requested_by: requestedBy
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async resolveOperatingProcedure({ procedureKey, intentText, requestedBy } = {}) {
|
|
92
|
+
return this._request('/api/operator/retrieval/wrapper/operating-procedure', {
|
|
93
|
+
query: {
|
|
94
|
+
procedure_key: procedureKey,
|
|
95
|
+
intent_text: intentText,
|
|
96
|
+
requested_by: requestedBy
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getSymbolDefinition({ symbolKey, qualifiedName, symbolName, projectScope, includeCode = true, maxLines = 120, requestedBy } = {}) {
|
|
102
|
+
return this._request('/api/operator/retrieval/wrapper/symbol-definition', {
|
|
103
|
+
query: {
|
|
104
|
+
symbol_key: symbolKey,
|
|
105
|
+
qualified_name: qualifiedName,
|
|
106
|
+
symbol_name: symbolName,
|
|
107
|
+
project_scope: projectScope,
|
|
108
|
+
include_code: includeCode,
|
|
109
|
+
max_lines: maxLines,
|
|
110
|
+
requested_by: requestedBy
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getRelatedCode({ symbolKey, qualifiedName, relationshipType, depth = 1, includeCode = false, maxLines = 80, requestedBy } = {}) {
|
|
116
|
+
return this._request('/api/operator/retrieval/wrapper/related-code', {
|
|
117
|
+
query: {
|
|
118
|
+
symbol_key: symbolKey,
|
|
119
|
+
qualified_name: qualifiedName,
|
|
120
|
+
relationship_type: relationshipType,
|
|
121
|
+
depth,
|
|
122
|
+
include_code: includeCode,
|
|
123
|
+
max_lines: maxLines,
|
|
124
|
+
requested_by: requestedBy
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async _request(path, { method = 'GET', query, headers, body } = {}) {
|
|
130
|
+
const url = appendQuery(`${this.baseUrl}${path}`, query);
|
|
131
|
+
const controller = new AbortController();
|
|
132
|
+
const timeoutHandle = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
133
|
+
try {
|
|
134
|
+
const response = await this.fetchImpl(url, {
|
|
135
|
+
method,
|
|
136
|
+
headers: {
|
|
137
|
+
accept: 'application/json',
|
|
138
|
+
'content-type': body ? 'application/json' : undefined,
|
|
139
|
+
'x-actor-id': this.actorId,
|
|
140
|
+
authorization: this.apiKey ? `Bearer ${this.apiKey}` : undefined,
|
|
141
|
+
...headers
|
|
142
|
+
},
|
|
143
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
144
|
+
signal: controller.signal
|
|
145
|
+
});
|
|
146
|
+
const payload = await readJson(response);
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
const error = new Error(payload?.message || payload?.error || `Request failed with status ${response.status}.`);
|
|
149
|
+
error.status = response.status;
|
|
150
|
+
error.payload = payload;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
return payload;
|
|
154
|
+
} finally {
|
|
155
|
+
clearTimeout(timeoutHandle);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function createAIEngineClient(options) {
|
|
161
|
+
return new AIEngineClient(options);
|
|
162
|
+
}
|