@axonflow/sdk 1.2.0 → 1.2.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/LICENSE +21 -0
- package/README.md +194 -9
- package/dist/cjs/client.d.ts +64 -40
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +279 -198
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.d.ts +51 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +84 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interceptors/anthropic.d.ts +1 -1
- package/dist/cjs/interceptors/anthropic.d.ts.map +1 -1
- package/dist/cjs/interceptors/anthropic.js +6 -6
- package/dist/cjs/interceptors/anthropic.js.map +1 -1
- package/dist/cjs/interceptors/openai.d.ts +1 -1
- package/dist/cjs/interceptors/openai.d.ts.map +1 -1
- package/dist/cjs/interceptors/openai.js +5 -5
- package/dist/cjs/interceptors/openai.js.map +1 -1
- package/dist/cjs/types/config.d.ts +7 -1
- package/dist/cjs/types/config.d.ts.map +1 -1
- package/dist/cjs/types/gateway.d.ts +51 -114
- package/dist/cjs/types/gateway.d.ts.map +1 -1
- package/dist/cjs/types/gateway.js +2 -7
- package/dist/cjs/types/gateway.js.map +1 -1
- package/dist/cjs/utils/helpers.d.ts.map +1 -1
- package/dist/cjs/utils/helpers.js +3 -1
- package/dist/cjs/utils/helpers.js.map +1 -1
- package/dist/esm/client.d.ts +64 -40
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +279 -198
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +51 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +75 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interceptors/anthropic.d.ts +1 -1
- package/dist/esm/interceptors/anthropic.d.ts.map +1 -1
- package/dist/esm/interceptors/anthropic.js +6 -6
- package/dist/esm/interceptors/anthropic.js.map +1 -1
- package/dist/esm/interceptors/openai.d.ts +1 -1
- package/dist/esm/interceptors/openai.d.ts.map +1 -1
- package/dist/esm/interceptors/openai.js +5 -5
- package/dist/esm/interceptors/openai.js.map +1 -1
- package/dist/esm/types/config.d.ts +7 -1
- package/dist/esm/types/config.d.ts.map +1 -1
- package/dist/esm/types/gateway.d.ts +51 -114
- package/dist/esm/types/gateway.d.ts.map +1 -1
- package/dist/esm/types/gateway.js +2 -7
- package/dist/esm/types/gateway.js.map +1 -1
- package/dist/esm/utils/helpers.d.ts.map +1 -1
- package/dist/esm/utils/helpers.js +3 -1
- package/dist/esm/utils/helpers.js.map +1 -1
- package/package.json +21 -7
package/dist/cjs/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AxonFlow = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
4
5
|
const openai_1 = require("./interceptors/openai");
|
|
5
6
|
const anthropic_1 = require("./interceptors/anthropic");
|
|
6
7
|
const helpers_1 = require("./utils/helpers");
|
|
@@ -10,24 +11,49 @@ const helpers_1 = require("./utils/helpers");
|
|
|
10
11
|
class AxonFlow {
|
|
11
12
|
constructor(config) {
|
|
12
13
|
this.interceptors = [];
|
|
13
|
-
// Set defaults
|
|
14
|
+
// Set defaults first to determine endpoint
|
|
15
|
+
const endpoint = config.endpoint || 'https://staging-eu.getaxonflow.com';
|
|
16
|
+
// Check if running in self-hosted mode (localhost)
|
|
17
|
+
const isLocalhost = endpoint.includes('localhost') || endpoint.includes('127.0.0.1');
|
|
18
|
+
// License key is optional for self-hosted deployments
|
|
19
|
+
// When not provided, agent must have SELF_HOSTED_MODE=true
|
|
20
|
+
if (!isLocalhost && !config.licenseKey && !config.apiKey) {
|
|
21
|
+
throw new Error('Either licenseKey or apiKey must be provided for non-localhost endpoints');
|
|
22
|
+
}
|
|
23
|
+
if (isLocalhost && !config.licenseKey && !config.apiKey && config.debug) {
|
|
24
|
+
console.warn('[AxonFlow] No license key provided - ensure agent has SELF_HOSTED_MODE=true');
|
|
25
|
+
}
|
|
26
|
+
// Set configuration
|
|
14
27
|
this.config = {
|
|
15
28
|
apiKey: config.apiKey,
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
licenseKey: config.licenseKey,
|
|
30
|
+
endpoint,
|
|
31
|
+
mode: config.mode || (isLocalhost ? 'sandbox' : 'production'),
|
|
18
32
|
tenant: config.tenant || 'default',
|
|
19
33
|
debug: config.debug || false,
|
|
20
34
|
timeout: config.timeout || 30000,
|
|
21
|
-
retry:
|
|
22
|
-
|
|
35
|
+
retry: {
|
|
36
|
+
enabled: config.retry?.enabled !== false,
|
|
37
|
+
maxAttempts: config.retry?.maxAttempts || 3,
|
|
38
|
+
delay: config.retry?.delay || 1000,
|
|
39
|
+
},
|
|
40
|
+
cache: {
|
|
41
|
+
enabled: config.cache?.enabled !== false,
|
|
42
|
+
ttl: config.cache?.ttl || 60000,
|
|
43
|
+
},
|
|
23
44
|
};
|
|
24
45
|
// Initialize interceptors
|
|
25
|
-
this.interceptors = [
|
|
26
|
-
new openai_1.OpenAIInterceptor(),
|
|
27
|
-
new anthropic_1.AnthropicInterceptor()
|
|
28
|
-
];
|
|
46
|
+
this.interceptors = [new openai_1.OpenAIInterceptor(), new anthropic_1.AnthropicInterceptor()];
|
|
29
47
|
if (this.config.debug) {
|
|
30
|
-
(0, helpers_1.debugLog)('AxonFlow initialized', {
|
|
48
|
+
(0, helpers_1.debugLog)('AxonFlow initialized', {
|
|
49
|
+
mode: this.config.mode,
|
|
50
|
+
endpoint: this.config.endpoint,
|
|
51
|
+
authMethod: isLocalhost
|
|
52
|
+
? 'self-hosted (no auth)'
|
|
53
|
+
: this.config.licenseKey
|
|
54
|
+
? 'license-key'
|
|
55
|
+
: 'api-key',
|
|
56
|
+
});
|
|
31
57
|
}
|
|
32
58
|
}
|
|
33
59
|
/**
|
|
@@ -48,7 +74,7 @@ class AxonFlow {
|
|
|
48
74
|
timestamp: Date.now(),
|
|
49
75
|
aiRequest,
|
|
50
76
|
mode: this.config.mode,
|
|
51
|
-
tenant: this.config.tenant
|
|
77
|
+
tenant: this.config.tenant,
|
|
52
78
|
};
|
|
53
79
|
// Check policies with AxonFlow Agent
|
|
54
80
|
const governanceResponse = await this.checkPolicies(governanceRequest);
|
|
@@ -93,7 +119,7 @@ class AxonFlow {
|
|
|
93
119
|
provider: 'unknown',
|
|
94
120
|
model: 'unknown',
|
|
95
121
|
prompt: aiCall.toString(),
|
|
96
|
-
parameters: {}
|
|
122
|
+
parameters: {},
|
|
97
123
|
};
|
|
98
124
|
}
|
|
99
125
|
/**
|
|
@@ -104,7 +130,7 @@ class AxonFlow {
|
|
|
104
130
|
// Transform SDK request to Agent API format
|
|
105
131
|
const agentRequest = {
|
|
106
132
|
query: request.aiRequest.prompt,
|
|
107
|
-
user_token: this.config.apiKey,
|
|
133
|
+
user_token: this.config.apiKey || '',
|
|
108
134
|
client_id: this.config.tenant,
|
|
109
135
|
request_type: 'llm_chat',
|
|
110
136
|
context: {
|
|
@@ -112,16 +138,23 @@ class AxonFlow {
|
|
|
112
138
|
model: request.aiRequest.model,
|
|
113
139
|
parameters: request.aiRequest.parameters,
|
|
114
140
|
requestId: request.requestId,
|
|
115
|
-
mode: this.config.mode
|
|
116
|
-
}
|
|
141
|
+
mode: this.config.mode,
|
|
142
|
+
},
|
|
117
143
|
};
|
|
144
|
+
const headers = {
|
|
145
|
+
'Content-Type': 'application/json',
|
|
146
|
+
};
|
|
147
|
+
// Add license key header if available (preferred auth method)
|
|
148
|
+
// Skip auth headers for localhost (self-hosted mode)
|
|
149
|
+
const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
|
|
150
|
+
if (!isLocalhost && this.config.licenseKey) {
|
|
151
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
152
|
+
}
|
|
118
153
|
const response = await fetch(url, {
|
|
119
154
|
method: 'POST',
|
|
120
|
-
headers
|
|
121
|
-
'Content-Type': 'application/json'
|
|
122
|
-
},
|
|
155
|
+
headers,
|
|
123
156
|
body: JSON.stringify(agentRequest),
|
|
124
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
157
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
125
158
|
});
|
|
126
159
|
if (!response.ok) {
|
|
127
160
|
const errorText = await response.text();
|
|
@@ -129,23 +162,29 @@ class AxonFlow {
|
|
|
129
162
|
}
|
|
130
163
|
const agentResponse = await response.json();
|
|
131
164
|
// Transform Agent API response to SDK format
|
|
165
|
+
// Extract policy name from policy_info if available
|
|
166
|
+
const policyName = agentResponse.policy_info?.policies_evaluated?.[0] || 'agent-policy';
|
|
132
167
|
return {
|
|
133
168
|
requestId: request.requestId,
|
|
134
169
|
allowed: !agentResponse.blocked,
|
|
135
|
-
violations: agentResponse.blocked
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
violations: agentResponse.blocked
|
|
171
|
+
? [
|
|
172
|
+
{
|
|
173
|
+
type: 'security',
|
|
174
|
+
severity: 'high',
|
|
175
|
+
description: agentResponse.block_reason || 'Request blocked by policy',
|
|
176
|
+
policy: policyName,
|
|
177
|
+
action: 'blocked',
|
|
178
|
+
},
|
|
179
|
+
]
|
|
180
|
+
: [],
|
|
142
181
|
modifiedRequest: agentResponse.data,
|
|
143
|
-
policies: [],
|
|
182
|
+
policies: agentResponse.policy_info?.policies_evaluated || [],
|
|
144
183
|
audit: {
|
|
145
184
|
timestamp: Date.now(),
|
|
146
185
|
duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '') || '0'),
|
|
147
|
-
tenant: this.config.tenant
|
|
148
|
-
}
|
|
186
|
+
tenant: this.config.tenant,
|
|
187
|
+
},
|
|
149
188
|
};
|
|
150
189
|
}
|
|
151
190
|
/**
|
|
@@ -158,7 +197,7 @@ class AxonFlow {
|
|
|
158
197
|
(0, helpers_1.debugLog)('Request processed', {
|
|
159
198
|
allowed: response.allowed,
|
|
160
199
|
violations: response.violations?.length || 0,
|
|
161
|
-
duration: response.audit.duration
|
|
200
|
+
duration: response.audit.duration,
|
|
162
201
|
});
|
|
163
202
|
}
|
|
164
203
|
}
|
|
@@ -166,9 +205,9 @@ class AxonFlow {
|
|
|
166
205
|
* Check if an error is from AxonFlow (vs the AI provider)
|
|
167
206
|
*/
|
|
168
207
|
isAxonFlowError(error) {
|
|
169
|
-
return error?.message?.includes('AxonFlow') ||
|
|
208
|
+
return (error?.message?.includes('AxonFlow') ||
|
|
170
209
|
error?.message?.includes('governance') ||
|
|
171
|
-
error?.message?.includes('fetch');
|
|
210
|
+
error?.message?.includes('fetch'));
|
|
172
211
|
}
|
|
173
212
|
/**
|
|
174
213
|
* Create a sandbox client for testing
|
|
@@ -178,7 +217,7 @@ class AxonFlow {
|
|
|
178
217
|
apiKey,
|
|
179
218
|
mode: 'sandbox',
|
|
180
219
|
endpoint: 'https://staging-eu.getaxonflow.com',
|
|
181
|
-
debug: true
|
|
220
|
+
debug: true,
|
|
182
221
|
});
|
|
183
222
|
}
|
|
184
223
|
/**
|
|
@@ -188,7 +227,7 @@ class AxonFlow {
|
|
|
188
227
|
const url = `${this.config.endpoint}/api/connectors`;
|
|
189
228
|
const response = await fetch(url, {
|
|
190
229
|
method: 'GET',
|
|
191
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
230
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
192
231
|
});
|
|
193
232
|
if (!response.ok) {
|
|
194
233
|
throw new Error(`Failed to list connectors: ${response.status} ${response.statusText}`);
|
|
@@ -204,14 +243,21 @@ class AxonFlow {
|
|
|
204
243
|
*/
|
|
205
244
|
async installConnector(request) {
|
|
206
245
|
const url = `${this.config.endpoint}/api/connectors/install`;
|
|
246
|
+
const headers = {
|
|
247
|
+
'Content-Type': 'application/json',
|
|
248
|
+
};
|
|
249
|
+
// Add authentication headers
|
|
250
|
+
if (this.config.licenseKey) {
|
|
251
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
252
|
+
}
|
|
253
|
+
else if (this.config.apiKey) {
|
|
254
|
+
headers['X-Client-Secret'] = this.config.apiKey;
|
|
255
|
+
}
|
|
207
256
|
const response = await fetch(url, {
|
|
208
257
|
method: 'POST',
|
|
209
|
-
headers
|
|
210
|
-
'Content-Type': 'application/json',
|
|
211
|
-
'X-Client-Secret': this.config.apiKey
|
|
212
|
-
},
|
|
258
|
+
headers,
|
|
213
259
|
body: JSON.stringify(request),
|
|
214
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
260
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
215
261
|
});
|
|
216
262
|
if (!response.ok) {
|
|
217
263
|
const errorText = await response.text();
|
|
@@ -227,22 +273,26 @@ class AxonFlow {
|
|
|
227
273
|
async queryConnector(connectorName, query, params) {
|
|
228
274
|
const agentRequest = {
|
|
229
275
|
query,
|
|
230
|
-
user_token: this.config.apiKey,
|
|
276
|
+
user_token: this.config.apiKey || '',
|
|
231
277
|
client_id: this.config.tenant,
|
|
232
278
|
request_type: 'mcp-query',
|
|
233
279
|
context: {
|
|
234
280
|
connector: connectorName,
|
|
235
|
-
params: params || {}
|
|
236
|
-
}
|
|
281
|
+
params: params || {},
|
|
282
|
+
},
|
|
237
283
|
};
|
|
238
284
|
const url = `${this.config.endpoint}/api/request`;
|
|
285
|
+
const headers = {
|
|
286
|
+
'Content-Type': 'application/json',
|
|
287
|
+
};
|
|
288
|
+
if (this.config.licenseKey) {
|
|
289
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
290
|
+
}
|
|
239
291
|
const response = await fetch(url, {
|
|
240
292
|
method: 'POST',
|
|
241
|
-
headers
|
|
242
|
-
'Content-Type': 'application/json'
|
|
243
|
-
},
|
|
293
|
+
headers,
|
|
244
294
|
body: JSON.stringify(agentRequest),
|
|
245
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
295
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
246
296
|
});
|
|
247
297
|
if (!response.ok) {
|
|
248
298
|
const errorText = await response.text();
|
|
@@ -256,28 +306,35 @@ class AxonFlow {
|
|
|
256
306
|
success: agentResponse.success,
|
|
257
307
|
data: agentResponse.data,
|
|
258
308
|
error: agentResponse.error,
|
|
259
|
-
meta: agentResponse.metadata
|
|
309
|
+
meta: agentResponse.metadata,
|
|
260
310
|
};
|
|
261
311
|
}
|
|
262
312
|
/**
|
|
263
313
|
* Generate a multi-agent execution plan from a natural language query
|
|
314
|
+
* @param query - Natural language query describing the task
|
|
315
|
+
* @param domain - Optional domain hint (travel, healthcare, etc.)
|
|
316
|
+
* @param userToken - Optional user token for authentication (defaults to tenant/client_id)
|
|
264
317
|
*/
|
|
265
|
-
async generatePlan(query, domain) {
|
|
318
|
+
async generatePlan(query, domain, userToken) {
|
|
266
319
|
const agentRequest = {
|
|
267
320
|
query,
|
|
268
|
-
user_token: this.config.
|
|
321
|
+
user_token: userToken || this.config.tenant,
|
|
269
322
|
client_id: this.config.tenant,
|
|
270
323
|
request_type: 'multi-agent-plan',
|
|
271
|
-
context: domain ? { domain } : {}
|
|
324
|
+
context: domain ? { domain } : {},
|
|
272
325
|
};
|
|
273
326
|
const url = `${this.config.endpoint}/api/request`;
|
|
327
|
+
const headers = {
|
|
328
|
+
'Content-Type': 'application/json',
|
|
329
|
+
};
|
|
330
|
+
if (this.config.licenseKey) {
|
|
331
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
332
|
+
}
|
|
274
333
|
const response = await fetch(url, {
|
|
275
334
|
method: 'POST',
|
|
276
|
-
headers
|
|
277
|
-
'Content-Type': 'application/json'
|
|
278
|
-
},
|
|
335
|
+
headers,
|
|
279
336
|
body: JSON.stringify(agentRequest),
|
|
280
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
337
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
281
338
|
});
|
|
282
339
|
if (!response.ok) {
|
|
283
340
|
const errorText = await response.text();
|
|
@@ -296,28 +353,34 @@ class AxonFlow {
|
|
|
296
353
|
domain: agentResponse.data?.domain || domain || 'generic',
|
|
297
354
|
complexity: agentResponse.data?.complexity || 0,
|
|
298
355
|
parallel: agentResponse.data?.parallel || false,
|
|
299
|
-
metadata: agentResponse.metadata || {}
|
|
356
|
+
metadata: agentResponse.metadata || {},
|
|
300
357
|
};
|
|
301
358
|
}
|
|
302
359
|
/**
|
|
303
360
|
* Execute a previously generated multi-agent plan
|
|
361
|
+
* @param planId - ID of the plan to execute
|
|
362
|
+
* @param userToken - Optional user token for authentication (defaults to tenant/client_id)
|
|
304
363
|
*/
|
|
305
|
-
async executePlan(planId) {
|
|
364
|
+
async executePlan(planId, userToken) {
|
|
306
365
|
const agentRequest = {
|
|
307
366
|
query: '',
|
|
308
|
-
user_token: this.config.
|
|
367
|
+
user_token: userToken || this.config.tenant,
|
|
309
368
|
client_id: this.config.tenant,
|
|
310
369
|
request_type: 'execute-plan',
|
|
311
|
-
context: { plan_id: planId }
|
|
370
|
+
context: { plan_id: planId },
|
|
312
371
|
};
|
|
313
372
|
const url = `${this.config.endpoint}/api/request`;
|
|
373
|
+
const headers = {
|
|
374
|
+
'Content-Type': 'application/json',
|
|
375
|
+
};
|
|
376
|
+
if (this.config.licenseKey) {
|
|
377
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
378
|
+
}
|
|
314
379
|
const response = await fetch(url, {
|
|
315
380
|
method: 'POST',
|
|
316
|
-
headers
|
|
317
|
-
'Content-Type': 'application/json'
|
|
318
|
-
},
|
|
381
|
+
headers,
|
|
319
382
|
body: JSON.stringify(agentRequest),
|
|
320
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
383
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
321
384
|
});
|
|
322
385
|
if (!response.ok) {
|
|
323
386
|
const errorText = await response.text();
|
|
@@ -333,7 +396,7 @@ class AxonFlow {
|
|
|
333
396
|
result: agentResponse.result,
|
|
334
397
|
stepResults: agentResponse.metadata?.step_results,
|
|
335
398
|
error: agentResponse.error,
|
|
336
|
-
duration: agentResponse.metadata?.duration
|
|
399
|
+
duration: agentResponse.metadata?.duration,
|
|
337
400
|
};
|
|
338
401
|
}
|
|
339
402
|
/**
|
|
@@ -343,7 +406,7 @@ class AxonFlow {
|
|
|
343
406
|
const url = `${this.config.endpoint}/api/plans/${planId}`;
|
|
344
407
|
const response = await fetch(url, {
|
|
345
408
|
method: 'GET',
|
|
346
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
409
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
347
410
|
});
|
|
348
411
|
if (!response.ok) {
|
|
349
412
|
const errorText = await response.text();
|
|
@@ -356,191 +419,209 @@ class AxonFlow {
|
|
|
356
419
|
result: status.result,
|
|
357
420
|
stepResults: status.step_results,
|
|
358
421
|
error: status.error,
|
|
359
|
-
duration: status.duration
|
|
422
|
+
duration: status.duration,
|
|
360
423
|
};
|
|
361
424
|
}
|
|
362
|
-
//
|
|
363
|
-
// Gateway Mode
|
|
364
|
-
//
|
|
365
|
-
// Gateway Mode allows clients to make LLM calls directly while still using
|
|
366
|
-
// AxonFlow for policy enforcement and audit logging.
|
|
367
|
-
//
|
|
368
|
-
// Usage:
|
|
369
|
-
// 1. Call getPolicyApprovedContext() before making LLM call
|
|
370
|
-
// 2. Make LLM call directly to your provider (using returned approved data)
|
|
371
|
-
// 3. Call auditLLMCall() after to record the call for compliance
|
|
372
|
-
//
|
|
373
|
-
// Example:
|
|
374
|
-
// const ctx = await axonflow.getPolicyApprovedContext({
|
|
375
|
-
// userToken: 'user-jwt',
|
|
376
|
-
// dataSources: ['postgres'],
|
|
377
|
-
// query: 'Find patients with diabetes'
|
|
378
|
-
// });
|
|
379
|
-
// if (!ctx.approved) throw new Error(ctx.blockReason);
|
|
380
|
-
//
|
|
381
|
-
// const llmResp = await openai.chat.completions.create({...}); // Your LLM call
|
|
382
|
-
//
|
|
383
|
-
// await axonflow.auditLLMCall({
|
|
384
|
-
// contextId: ctx.contextId,
|
|
385
|
-
// responseSummary: 'Found 5 patients',
|
|
386
|
-
// provider: 'openai',
|
|
387
|
-
// model: 'gpt-4',
|
|
388
|
-
// tokenUsage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
|
|
389
|
-
// latencyMs: 250
|
|
390
|
-
// });
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Gateway Mode Methods
|
|
427
|
+
// ============================================================================
|
|
391
428
|
/**
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
|
|
395
|
-
|
|
429
|
+
* Gateway Mode: Pre-check policy approval before making a direct LLM call.
|
|
430
|
+
* Alias for getPolicyApprovedContext() for simpler API.
|
|
431
|
+
*/
|
|
432
|
+
async preCheck(options) {
|
|
433
|
+
return this.getPolicyApprovedContext(options);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Gateway Mode: Get policy-approved context before making a direct LLM call.
|
|
396
437
|
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
438
|
+
* Use this when you want to:
|
|
439
|
+
* - Make direct LLM calls (not through AxonFlow proxy)
|
|
440
|
+
* - Have full control over your LLM provider/model selection
|
|
441
|
+
* - Minimize latency by calling LLM directly
|
|
399
442
|
*
|
|
400
443
|
* @example
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
* query: '
|
|
444
|
+
* ```typescript
|
|
445
|
+
* const ctx = await axonflow.getPolicyApprovedContext({
|
|
446
|
+
* userToken: 'user-jwt',
|
|
447
|
+
* query: 'Analyze this customer data',
|
|
448
|
+
* dataSources: ['postgres']
|
|
405
449
|
* });
|
|
406
450
|
*
|
|
407
|
-
* if (!
|
|
408
|
-
* throw new Error(`
|
|
451
|
+
* if (!ctx.approved) {
|
|
452
|
+
* throw new Error(`Blocked: ${ctx.blockReason}`);
|
|
409
453
|
* }
|
|
410
454
|
*
|
|
411
|
-
* //
|
|
412
|
-
* const
|
|
455
|
+
* // Make direct LLM call with approved data
|
|
456
|
+
* const response = await openai.chat.completions.create({
|
|
457
|
+
* model: 'gpt-4',
|
|
458
|
+
* messages: [{ role: 'user', content: JSON.stringify(ctx.approvedData) }]
|
|
459
|
+
* });
|
|
460
|
+
*
|
|
461
|
+
* // Audit the call
|
|
462
|
+
* await axonflow.auditLLMCall({
|
|
463
|
+
* contextId: ctx.contextId,
|
|
464
|
+
* responseSummary: response.choices[0].message.content.substring(0, 100),
|
|
465
|
+
* provider: 'openai',
|
|
466
|
+
* model: 'gpt-4',
|
|
467
|
+
* tokenUsage: {
|
|
468
|
+
* promptTokens: response.usage.prompt_tokens,
|
|
469
|
+
* completionTokens: response.usage.completion_tokens,
|
|
470
|
+
* totalTokens: response.usage.total_tokens
|
|
471
|
+
* },
|
|
472
|
+
* latencyMs: 250
|
|
473
|
+
* });
|
|
474
|
+
* ```
|
|
413
475
|
*/
|
|
414
|
-
async getPolicyApprovedContext(
|
|
476
|
+
async getPolicyApprovedContext(options) {
|
|
415
477
|
const url = `${this.config.endpoint}/api/policy/pre-check`;
|
|
416
|
-
const
|
|
417
|
-
user_token:
|
|
478
|
+
const requestBody = {
|
|
479
|
+
user_token: options.userToken,
|
|
418
480
|
client_id: this.config.tenant,
|
|
419
|
-
query:
|
|
420
|
-
data_sources:
|
|
421
|
-
context:
|
|
481
|
+
query: options.query,
|
|
482
|
+
data_sources: options.dataSources || [],
|
|
483
|
+
context: options.context || {},
|
|
484
|
+
};
|
|
485
|
+
const headers = {
|
|
486
|
+
'Content-Type': 'application/json',
|
|
422
487
|
};
|
|
488
|
+
// Add authentication headers
|
|
489
|
+
const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
|
|
490
|
+
if (!isLocalhost) {
|
|
491
|
+
if (this.config.licenseKey) {
|
|
492
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
493
|
+
}
|
|
494
|
+
else if (this.config.apiKey) {
|
|
495
|
+
headers['X-Client-Secret'] = this.config.apiKey;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
423
498
|
if (this.config.debug) {
|
|
424
|
-
(0, helpers_1.debugLog)('Gateway
|
|
425
|
-
query: request.query.substring(0, 50),
|
|
426
|
-
dataSources: request.dataSources
|
|
427
|
-
});
|
|
499
|
+
(0, helpers_1.debugLog)('Gateway Mode: Pre-check', { query: options.query.substring(0, 50) });
|
|
428
500
|
}
|
|
429
|
-
const startTime = Date.now();
|
|
430
501
|
const response = await fetch(url, {
|
|
431
502
|
method: 'POST',
|
|
432
|
-
headers
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
'X-License-Key': this.config.apiKey
|
|
436
|
-
},
|
|
437
|
-
body: JSON.stringify(body),
|
|
438
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
503
|
+
headers,
|
|
504
|
+
body: JSON.stringify(requestBody),
|
|
505
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
439
506
|
});
|
|
440
|
-
const duration = Date.now() - startTime;
|
|
441
507
|
if (!response.ok) {
|
|
442
508
|
const errorText = await response.text();
|
|
443
|
-
|
|
509
|
+
if (response.status === 401 || response.status === 403) {
|
|
510
|
+
throw new errors_1.AuthenticationError(`Policy pre-check authentication failed: ${errorText}`);
|
|
511
|
+
}
|
|
512
|
+
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
444
513
|
}
|
|
445
514
|
const data = await response.json();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
515
|
+
// Transform snake_case response to camelCase
|
|
516
|
+
// Default expiration to 5 minutes from now if not provided
|
|
517
|
+
const expiresAt = data.expires_at
|
|
518
|
+
? new Date(data.expires_at)
|
|
519
|
+
: new Date(Date.now() + 5 * 60 * 1000);
|
|
520
|
+
const result = {
|
|
454
521
|
contextId: data.context_id,
|
|
455
522
|
approved: data.approved,
|
|
456
|
-
approvedData: data.approved_data,
|
|
523
|
+
approvedData: data.approved_data || {},
|
|
457
524
|
policies: data.policies || [],
|
|
458
|
-
|
|
525
|
+
expiresAt,
|
|
526
|
+
blockReason: data.block_reason,
|
|
527
|
+
};
|
|
528
|
+
// Parse rate limit info if present
|
|
529
|
+
if (data.rate_limit) {
|
|
530
|
+
result.rateLimitInfo = {
|
|
459
531
|
limit: data.rate_limit.limit,
|
|
460
532
|
remaining: data.rate_limit.remaining,
|
|
461
|
-
resetAt: new Date(data.rate_limit.reset_at)
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
533
|
+
resetAt: new Date(data.rate_limit.reset_at),
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (this.config.debug) {
|
|
537
|
+
(0, helpers_1.debugLog)('Gateway Mode: Pre-check result', {
|
|
538
|
+
approved: result.approved,
|
|
539
|
+
contextId: result.contextId,
|
|
540
|
+
policies: result.policies.length,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
return result;
|
|
466
544
|
}
|
|
467
545
|
/**
|
|
468
|
-
*
|
|
546
|
+
* Gateway Mode: Audit an LLM call after completion.
|
|
469
547
|
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* @param contextId Context ID from getPolicyApprovedContext()
|
|
474
|
-
* @param responseSummary Brief summary of the LLM response (not full response)
|
|
475
|
-
* @param provider LLM provider name
|
|
476
|
-
* @param model Model name
|
|
477
|
-
* @param tokenUsage Token counts from LLM response
|
|
478
|
-
* @param latencyMs Time taken for LLM call in milliseconds
|
|
479
|
-
* @param metadata Optional additional metadata
|
|
480
|
-
* @returns AuditResult confirming the audit was recorded
|
|
548
|
+
* Call this after making a direct LLM call to log the audit trail.
|
|
549
|
+
* This is required for compliance and monitoring.
|
|
481
550
|
*
|
|
482
551
|
* @example
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* '
|
|
487
|
-
* '
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
552
|
+
* ```typescript
|
|
553
|
+
* await axonflow.auditLLMCall({
|
|
554
|
+
* contextId: ctx.contextId,
|
|
555
|
+
* responseSummary: 'Generated report with 5 items',
|
|
556
|
+
* provider: 'openai',
|
|
557
|
+
* model: 'gpt-4',
|
|
558
|
+
* tokenUsage: {
|
|
559
|
+
* promptTokens: 100,
|
|
560
|
+
* completionTokens: 50,
|
|
561
|
+
* totalTokens: 150
|
|
562
|
+
* },
|
|
563
|
+
* latencyMs: 250
|
|
564
|
+
* });
|
|
565
|
+
* ```
|
|
492
566
|
*/
|
|
493
|
-
async auditLLMCall(
|
|
567
|
+
async auditLLMCall(options) {
|
|
494
568
|
const url = `${this.config.endpoint}/api/audit/llm-call`;
|
|
495
|
-
const
|
|
496
|
-
context_id: contextId,
|
|
569
|
+
const requestBody = {
|
|
570
|
+
context_id: options.contextId,
|
|
497
571
|
client_id: this.config.tenant,
|
|
498
|
-
response_summary: responseSummary,
|
|
499
|
-
provider,
|
|
500
|
-
model,
|
|
572
|
+
response_summary: options.responseSummary,
|
|
573
|
+
provider: options.provider,
|
|
574
|
+
model: options.model,
|
|
501
575
|
token_usage: {
|
|
502
|
-
prompt_tokens: tokenUsage.promptTokens,
|
|
503
|
-
completion_tokens: tokenUsage.completionTokens,
|
|
504
|
-
total_tokens: tokenUsage.totalTokens
|
|
576
|
+
prompt_tokens: options.tokenUsage.promptTokens,
|
|
577
|
+
completion_tokens: options.tokenUsage.completionTokens,
|
|
578
|
+
total_tokens: options.tokenUsage.totalTokens,
|
|
505
579
|
},
|
|
506
|
-
latency_ms: latencyMs,
|
|
507
|
-
metadata
|
|
580
|
+
latency_ms: options.latencyMs,
|
|
581
|
+
metadata: options.metadata || {},
|
|
508
582
|
};
|
|
583
|
+
const headers = {
|
|
584
|
+
'Content-Type': 'application/json',
|
|
585
|
+
};
|
|
586
|
+
// Add authentication headers
|
|
587
|
+
const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
|
|
588
|
+
if (!isLocalhost) {
|
|
589
|
+
if (this.config.licenseKey) {
|
|
590
|
+
headers['X-License-Key'] = this.config.licenseKey;
|
|
591
|
+
}
|
|
592
|
+
else if (this.config.apiKey) {
|
|
593
|
+
headers['X-Client-Secret'] = this.config.apiKey;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
509
596
|
if (this.config.debug) {
|
|
510
|
-
(0, helpers_1.debugLog)('Gateway
|
|
511
|
-
contextId,
|
|
512
|
-
provider,
|
|
513
|
-
model,
|
|
514
|
-
tokens: tokenUsage.totalTokens
|
|
597
|
+
(0, helpers_1.debugLog)('Gateway Mode: Audit', {
|
|
598
|
+
contextId: options.contextId,
|
|
599
|
+
provider: options.provider,
|
|
600
|
+
model: options.model,
|
|
515
601
|
});
|
|
516
602
|
}
|
|
517
|
-
const startTime = Date.now();
|
|
518
603
|
const response = await fetch(url, {
|
|
519
604
|
method: 'POST',
|
|
520
|
-
headers
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
'X-License-Key': this.config.apiKey
|
|
524
|
-
},
|
|
525
|
-
body: JSON.stringify(body),
|
|
526
|
-
signal: AbortSignal.timeout(this.config.timeout)
|
|
605
|
+
headers,
|
|
606
|
+
body: JSON.stringify(requestBody),
|
|
607
|
+
signal: AbortSignal.timeout(this.config.timeout),
|
|
527
608
|
});
|
|
528
|
-
const duration = Date.now() - startTime;
|
|
529
609
|
if (!response.ok) {
|
|
530
610
|
const errorText = await response.text();
|
|
531
|
-
|
|
611
|
+
if (response.status === 401 || response.status === 403) {
|
|
612
|
+
throw new errors_1.AuthenticationError(`Audit logging authentication failed: ${errorText}`);
|
|
613
|
+
}
|
|
614
|
+
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
532
615
|
}
|
|
533
616
|
const data = await response.json();
|
|
534
|
-
|
|
535
|
-
(0, helpers_1.debugLog)('Gateway audit complete', {
|
|
536
|
-
auditId: data.audit_id,
|
|
537
|
-
duration
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
return {
|
|
617
|
+
const result = {
|
|
541
618
|
success: data.success,
|
|
542
|
-
auditId: data.audit_id
|
|
619
|
+
auditId: data.audit_id,
|
|
543
620
|
};
|
|
621
|
+
if (this.config.debug) {
|
|
622
|
+
(0, helpers_1.debugLog)('Gateway Mode: Audit logged', { auditId: result.auditId });
|
|
623
|
+
}
|
|
624
|
+
return result;
|
|
544
625
|
}
|
|
545
626
|
}
|
|
546
627
|
exports.AxonFlow = AxonFlow;
|