@signalvaultio/node 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 +103 -0
- package/dist/__tests__/index.test.d.ts +1 -0
- package/dist/__tests__/index.test.js +49 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +335 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @signalvaultio/node
|
|
2
|
+
|
|
3
|
+
AI audit logs and guardrails for your OpenAI applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @signalvaultio/node openai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import SignalVaultClient from '@signalvaultio/node';
|
|
15
|
+
|
|
16
|
+
const client = new SignalVaultClient({
|
|
17
|
+
apiKey: 'sk_live_your_signalvault_key', // Your SignalVault API key
|
|
18
|
+
openaiApiKey: process.env.OPENAI_API_KEY!, // Your OpenAI API key
|
|
19
|
+
baseUrl: 'https://api.signalvault.io', // SignalVault API URL
|
|
20
|
+
environment: 'production',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Use exactly like OpenAI SDK — all calls are logged and guarded
|
|
24
|
+
const response = await client.chat.completions.create({
|
|
25
|
+
model: 'gpt-4',
|
|
26
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log(response.choices[0].message.content);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Streaming
|
|
33
|
+
|
|
34
|
+
Streaming responses are fully supported. SignalVault logs the complete response once the stream finishes:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const stream = await client.chat.completions.create({
|
|
38
|
+
model: 'gpt-4',
|
|
39
|
+
messages: [{ role: 'user', content: 'Write a poem' }],
|
|
40
|
+
stream: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for await (const chunk of stream) {
|
|
44
|
+
process.stdout.write(chunk.choices[0]?.delta?.content || '');
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Mirror Mode
|
|
49
|
+
|
|
50
|
+
In mirror mode, requests go directly to OpenAI first and SignalVault audits them asynchronously — no latency added, never blocks:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const client = new SignalVaultClient({
|
|
54
|
+
apiKey: 'sk_live_...',
|
|
55
|
+
openaiApiKey: process.env.OPENAI_API_KEY!,
|
|
56
|
+
mirrorMode: true, // Monitor only, never block
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **Automatic Logging** — Every request and response is recorded in your SignalVault dashboard
|
|
63
|
+
- **Pre-flight Guardrails** — Block or redact requests before they reach OpenAI
|
|
64
|
+
- **PII Detection** — Automatically detect emails, phone numbers, SSNs in prompts
|
|
65
|
+
- **Secret Detection** — Block API keys and tokens in prompts
|
|
66
|
+
- **Token Limits** — Enforce cost controls per request
|
|
67
|
+
- **Model Allowlists** — Restrict which AI models can be used
|
|
68
|
+
- **Streaming Support** — Full support for streaming completions
|
|
69
|
+
- **Mirror Mode** — Observe without blocking (zero added latency)
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const client = new SignalVaultClient({
|
|
75
|
+
apiKey: 'sk_live_...', // Your SignalVault API key
|
|
76
|
+
openaiApiKey: 'sk-...', // Your OpenAI API key
|
|
77
|
+
baseUrl: 'https://api.signalvault.io', // SignalVault API URL
|
|
78
|
+
environment: 'production', // 'development' | 'staging' | 'production'
|
|
79
|
+
debug: false, // Enable debug logging
|
|
80
|
+
mirrorMode: false, // Monitor-only mode
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Error Handling
|
|
85
|
+
|
|
86
|
+
If a request is blocked by a guardrail, an error will be thrown:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
try {
|
|
90
|
+
const response = await client.chat.completions.create({
|
|
91
|
+
model: 'gpt-4',
|
|
92
|
+
messages: [{ role: 'user', content: 'my SSN is 123-45-6789' }],
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error.message.includes('[SignalVault]')) {
|
|
96
|
+
console.log('Request blocked by guardrail');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../index");
|
|
4
|
+
// Minimal unit tests for the SDK (no network calls)
|
|
5
|
+
describe('SignalVaultClient', () => {
|
|
6
|
+
it('should construct with required config', () => {
|
|
7
|
+
const client = new index_1.SignalVaultClient({
|
|
8
|
+
apiKey: 'sk_test_abc',
|
|
9
|
+
openaiApiKey: 'sk-fake',
|
|
10
|
+
});
|
|
11
|
+
expect(client).toBeDefined();
|
|
12
|
+
expect(client.chat).toBeDefined();
|
|
13
|
+
expect(client.chat.completions).toBeDefined();
|
|
14
|
+
expect(typeof client.chat.completions.create).toBe('function');
|
|
15
|
+
});
|
|
16
|
+
it('should default to localhost and production', () => {
|
|
17
|
+
const client = new index_1.SignalVaultClient({
|
|
18
|
+
apiKey: 'sk_test_abc',
|
|
19
|
+
openaiApiKey: 'sk-fake',
|
|
20
|
+
});
|
|
21
|
+
// Access private fields via bracket notation for testing
|
|
22
|
+
expect(client.signalvaultBaseUrl).toBe('http://localhost:4000');
|
|
23
|
+
expect(client.signalvaultEnvironment).toBe('production');
|
|
24
|
+
expect(client.debugMode).toBe(false);
|
|
25
|
+
expect(client.mirrorMode).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('should accept custom baseUrl and environment', () => {
|
|
28
|
+
const client = new index_1.SignalVaultClient({
|
|
29
|
+
apiKey: 'sk_test_abc',
|
|
30
|
+
openaiApiKey: 'sk-fake',
|
|
31
|
+
baseUrl: 'https://api.signalvault.io/',
|
|
32
|
+
environment: 'staging',
|
|
33
|
+
debug: true,
|
|
34
|
+
mirrorMode: true,
|
|
35
|
+
});
|
|
36
|
+
expect(client.signalvaultBaseUrl).toBe('https://api.signalvault.io');
|
|
37
|
+
expect(client.signalvaultEnvironment).toBe('staging');
|
|
38
|
+
expect(client.debugMode).toBe(true);
|
|
39
|
+
expect(client.mirrorMode).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('should strip trailing slashes from baseUrl', () => {
|
|
42
|
+
const client = new index_1.SignalVaultClient({
|
|
43
|
+
apiKey: 'sk_test_abc',
|
|
44
|
+
openaiApiKey: 'sk-fake',
|
|
45
|
+
baseUrl: 'https://example.com///',
|
|
46
|
+
});
|
|
47
|
+
expect(client.signalvaultBaseUrl).toBe('https://example.com');
|
|
48
|
+
});
|
|
49
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
export interface SignalVaultConfig {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
openaiApiKey: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
environment?: 'development' | 'staging' | 'production';
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
mirrorMode?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface SignalVaultDecision {
|
|
11
|
+
decision: 'allow' | 'warn' | 'block' | 'redact';
|
|
12
|
+
violations: Array<{
|
|
13
|
+
rule_id?: string;
|
|
14
|
+
type: string;
|
|
15
|
+
severity: number;
|
|
16
|
+
action: string;
|
|
17
|
+
details: Record<string, unknown>;
|
|
18
|
+
}>;
|
|
19
|
+
redactions: Array<{
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
replacement: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* SignalVault Client — wraps OpenAI with guardrails and audit logging.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const client = new SignalVaultClient({
|
|
31
|
+
* apiKey: 'sk_live_...',
|
|
32
|
+
* openaiApiKey: process.env.OPENAI_API_KEY!,
|
|
33
|
+
* baseUrl: 'https://api.signalvault.io',
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Non-streaming
|
|
37
|
+
* const res = await client.chat.completions.create({ model: 'gpt-4', messages: [...] });
|
|
38
|
+
*
|
|
39
|
+
* // Streaming
|
|
40
|
+
* const stream = await client.chat.completions.create({ model: 'gpt-4', messages: [...], stream: true });
|
|
41
|
+
* for await (const chunk of stream) { ... }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare class SignalVaultClient {
|
|
45
|
+
private signalvaultApiKey;
|
|
46
|
+
private signalvaultBaseUrl;
|
|
47
|
+
private signalvaultEnvironment;
|
|
48
|
+
private debugMode;
|
|
49
|
+
private mirrorMode;
|
|
50
|
+
private openai;
|
|
51
|
+
constructor(config: SignalVaultConfig);
|
|
52
|
+
/**
|
|
53
|
+
* Chat completions with SignalVault guardrails.
|
|
54
|
+
* Supports both streaming and non-streaming.
|
|
55
|
+
*/
|
|
56
|
+
get chat(): {
|
|
57
|
+
completions: {
|
|
58
|
+
create: (params: OpenAI.Chat.ChatCompletionCreateParams) => Promise<any>;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
private handleMirrorMode;
|
|
62
|
+
private handleNormalMode;
|
|
63
|
+
private wrapStreamForAudit;
|
|
64
|
+
private wrapStreamForPostFlight;
|
|
65
|
+
private sendRequest;
|
|
66
|
+
private sendResponse;
|
|
67
|
+
private sendStreamResponse;
|
|
68
|
+
private sendAudit;
|
|
69
|
+
private sendStreamAudit;
|
|
70
|
+
}
|
|
71
|
+
export default SignalVaultClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SignalVaultClient = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
const uuid_1 = require("uuid");
|
|
9
|
+
/**
|
|
10
|
+
* SignalVault Client — wraps OpenAI with guardrails and audit logging.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const client = new SignalVaultClient({
|
|
15
|
+
* apiKey: 'sk_live_...',
|
|
16
|
+
* openaiApiKey: process.env.OPENAI_API_KEY!,
|
|
17
|
+
* baseUrl: 'https://api.signalvault.io',
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Non-streaming
|
|
21
|
+
* const res = await client.chat.completions.create({ model: 'gpt-4', messages: [...] });
|
|
22
|
+
*
|
|
23
|
+
* // Streaming
|
|
24
|
+
* const stream = await client.chat.completions.create({ model: 'gpt-4', messages: [...], stream: true });
|
|
25
|
+
* for await (const chunk of stream) { ... }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class SignalVaultClient {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.signalvaultApiKey = config.apiKey;
|
|
31
|
+
this.signalvaultBaseUrl = (config.baseUrl || 'http://localhost:4000').replace(/\/+$/, '');
|
|
32
|
+
this.signalvaultEnvironment = config.environment || 'production';
|
|
33
|
+
this.debugMode = config.debug || false;
|
|
34
|
+
this.mirrorMode = config.mirrorMode || false;
|
|
35
|
+
this.openai = new openai_1.default({
|
|
36
|
+
apiKey: config.openaiApiKey,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Chat completions with SignalVault guardrails.
|
|
41
|
+
* Supports both streaming and non-streaming.
|
|
42
|
+
*/
|
|
43
|
+
get chat() {
|
|
44
|
+
return {
|
|
45
|
+
completions: {
|
|
46
|
+
create: async (params) => {
|
|
47
|
+
const requestId = (0, uuid_1.v4)();
|
|
48
|
+
if (this.debugMode) {
|
|
49
|
+
console.log('[SignalVault] Processing request:', requestId, this.mirrorMode ? '(MIRROR MODE)' : '', params.stream ? '(STREAMING)' : '');
|
|
50
|
+
}
|
|
51
|
+
// Mirror mode: call OpenAI first, then audit
|
|
52
|
+
if (this.mirrorMode) {
|
|
53
|
+
return this.handleMirrorMode(requestId, params);
|
|
54
|
+
}
|
|
55
|
+
// Normal mode: pre-flight checks first
|
|
56
|
+
return this.handleNormalMode(requestId, params);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Mirror mode — no latency added, audit asynchronously
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
async handleMirrorMode(requestId, params) {
|
|
65
|
+
try {
|
|
66
|
+
const response = await this.openai.chat.completions.create(params);
|
|
67
|
+
if (params.stream) {
|
|
68
|
+
// Wrap the stream to collect chunks for auditing
|
|
69
|
+
return this.wrapStreamForAudit(requestId, params, response);
|
|
70
|
+
}
|
|
71
|
+
// Non-streaming: audit in background
|
|
72
|
+
const completion = response;
|
|
73
|
+
this.sendAudit(requestId, params, completion).catch((err) => {
|
|
74
|
+
if (this.debugMode) {
|
|
75
|
+
console.error('[SignalVault] Audit failed (non-blocking):', err);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return completion;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (this.debugMode) {
|
|
82
|
+
console.error('[SignalVault] OpenAI error:', error);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Normal mode — pre-flight guardrails, then OpenAI, then post-flight
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
async handleNormalMode(requestId, params) {
|
|
91
|
+
try {
|
|
92
|
+
// Pre-flight check
|
|
93
|
+
const decision = await this.sendRequest(requestId, params);
|
|
94
|
+
if (decision.decision === 'block') {
|
|
95
|
+
throw new Error(`[SignalVault] Request blocked: ${JSON.stringify(decision.violations)}`);
|
|
96
|
+
}
|
|
97
|
+
if (decision.decision === 'warn' && this.debugMode) {
|
|
98
|
+
console.warn('[SignalVault] Warnings:', decision.violations);
|
|
99
|
+
}
|
|
100
|
+
const response = await this.openai.chat.completions.create(params);
|
|
101
|
+
if (params.stream) {
|
|
102
|
+
// Wrap stream to collect output for post-flight logging
|
|
103
|
+
return this.wrapStreamForPostFlight(requestId, params, response);
|
|
104
|
+
}
|
|
105
|
+
// Non-streaming: post-flight logging
|
|
106
|
+
const completion = response;
|
|
107
|
+
await this.sendResponse(requestId, completion, params.model);
|
|
108
|
+
return completion;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (this.debugMode) {
|
|
112
|
+
console.error('[SignalVault] Error:', error);
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Stream wrappers — collect chunks and send audit/post-flight events
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
async *wrapStreamForAudit(requestId, params, stream) {
|
|
121
|
+
const chunks = [];
|
|
122
|
+
let promptTokens = 0;
|
|
123
|
+
let completionTokens = 0;
|
|
124
|
+
for await (const chunk of stream) {
|
|
125
|
+
const content = chunk.choices[0]?.delta?.content || '';
|
|
126
|
+
if (content)
|
|
127
|
+
chunks.push(content);
|
|
128
|
+
// Collect usage if provided in final chunk
|
|
129
|
+
if (chunk.usage) {
|
|
130
|
+
promptTokens = chunk.usage.prompt_tokens || 0;
|
|
131
|
+
completionTokens = chunk.usage.completion_tokens || 0;
|
|
132
|
+
}
|
|
133
|
+
yield chunk;
|
|
134
|
+
}
|
|
135
|
+
// Send audit after stream completes
|
|
136
|
+
const fullOutput = chunks.join('');
|
|
137
|
+
this.sendStreamAudit(requestId, params, fullOutput, promptTokens, completionTokens).catch((err) => {
|
|
138
|
+
if (this.debugMode) {
|
|
139
|
+
console.error('[SignalVault] Stream audit failed:', err);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async *wrapStreamForPostFlight(requestId, params, stream) {
|
|
144
|
+
const chunks = [];
|
|
145
|
+
let promptTokens = 0;
|
|
146
|
+
let completionTokens = 0;
|
|
147
|
+
for await (const chunk of stream) {
|
|
148
|
+
const content = chunk.choices[0]?.delta?.content || '';
|
|
149
|
+
if (content)
|
|
150
|
+
chunks.push(content);
|
|
151
|
+
if (chunk.usage) {
|
|
152
|
+
promptTokens = chunk.usage.prompt_tokens || 0;
|
|
153
|
+
completionTokens = chunk.usage.completion_tokens || 0;
|
|
154
|
+
}
|
|
155
|
+
yield chunk;
|
|
156
|
+
}
|
|
157
|
+
// Post-flight logging
|
|
158
|
+
const fullOutput = chunks.join('');
|
|
159
|
+
try {
|
|
160
|
+
await this.sendStreamResponse(requestId, params.model, fullOutput, promptTokens, completionTokens);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (this.debugMode) {
|
|
164
|
+
console.error('[SignalVault] Post-flight logging failed:', err);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// API communication
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
async sendRequest(requestId, params) {
|
|
172
|
+
const response = await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
173
|
+
method: 'POST',
|
|
174
|
+
headers: {
|
|
175
|
+
'Content-Type': 'application/json',
|
|
176
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
type: 'ai.request',
|
|
180
|
+
request_id: requestId,
|
|
181
|
+
environment: this.signalvaultEnvironment,
|
|
182
|
+
provider: 'openai',
|
|
183
|
+
model: params.model,
|
|
184
|
+
metadata: {},
|
|
185
|
+
payload: { messages: params.messages },
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
if (this.debugMode) {
|
|
190
|
+
console.error('[SignalVault] API error:', response.status);
|
|
191
|
+
}
|
|
192
|
+
return { decision: 'allow', violations: [], redactions: [] };
|
|
193
|
+
}
|
|
194
|
+
return (await response.json());
|
|
195
|
+
}
|
|
196
|
+
async sendResponse(requestId, response, model) {
|
|
197
|
+
try {
|
|
198
|
+
const output = response.choices[0]?.message?.content || '';
|
|
199
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: {
|
|
202
|
+
'Content-Type': 'application/json',
|
|
203
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
204
|
+
},
|
|
205
|
+
body: JSON.stringify({
|
|
206
|
+
type: 'ai.response',
|
|
207
|
+
request_id: requestId,
|
|
208
|
+
environment: this.signalvaultEnvironment,
|
|
209
|
+
provider: 'openai',
|
|
210
|
+
model,
|
|
211
|
+
metadata: {},
|
|
212
|
+
payload: {
|
|
213
|
+
output,
|
|
214
|
+
usage: {
|
|
215
|
+
prompt_tokens: response.usage?.prompt_tokens || 0,
|
|
216
|
+
completion_tokens: response.usage?.completion_tokens || 0,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
}),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (this.debugMode) {
|
|
224
|
+
console.error('[SignalVault] Failed to send response:', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async sendStreamResponse(requestId, model, output, promptTokens, completionTokens) {
|
|
229
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
type: 'ai.response',
|
|
237
|
+
request_id: requestId,
|
|
238
|
+
environment: this.signalvaultEnvironment,
|
|
239
|
+
provider: 'openai',
|
|
240
|
+
model,
|
|
241
|
+
metadata: {},
|
|
242
|
+
payload: {
|
|
243
|
+
output,
|
|
244
|
+
usage: { prompt_tokens: promptTokens, completion_tokens: completionTokens },
|
|
245
|
+
},
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async sendAudit(requestId, params, response) {
|
|
250
|
+
const output = response.choices[0]?.message?.content || '';
|
|
251
|
+
// Send ai.request event
|
|
252
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: {
|
|
255
|
+
'Content-Type': 'application/json',
|
|
256
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
257
|
+
},
|
|
258
|
+
body: JSON.stringify({
|
|
259
|
+
type: 'ai.request',
|
|
260
|
+
request_id: requestId,
|
|
261
|
+
environment: this.signalvaultEnvironment,
|
|
262
|
+
provider: 'openai',
|
|
263
|
+
model: params.model,
|
|
264
|
+
metadata: {},
|
|
265
|
+
payload: { messages: params.messages, monitor_mode: true },
|
|
266
|
+
}),
|
|
267
|
+
});
|
|
268
|
+
// Send ai.response event
|
|
269
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
270
|
+
method: 'POST',
|
|
271
|
+
headers: {
|
|
272
|
+
'Content-Type': 'application/json',
|
|
273
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
274
|
+
},
|
|
275
|
+
body: JSON.stringify({
|
|
276
|
+
type: 'ai.response',
|
|
277
|
+
request_id: requestId,
|
|
278
|
+
environment: this.signalvaultEnvironment,
|
|
279
|
+
provider: 'openai',
|
|
280
|
+
model: params.model,
|
|
281
|
+
metadata: {},
|
|
282
|
+
payload: {
|
|
283
|
+
output,
|
|
284
|
+
usage: {
|
|
285
|
+
prompt_tokens: response.usage?.prompt_tokens || 0,
|
|
286
|
+
completion_tokens: response.usage?.completion_tokens || 0,
|
|
287
|
+
},
|
|
288
|
+
monitor_mode: true,
|
|
289
|
+
},
|
|
290
|
+
}),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async sendStreamAudit(requestId, params, output, promptTokens, completionTokens) {
|
|
294
|
+
// Send ai.request event
|
|
295
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: {
|
|
298
|
+
'Content-Type': 'application/json',
|
|
299
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
300
|
+
},
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
type: 'ai.request',
|
|
303
|
+
request_id: requestId,
|
|
304
|
+
environment: this.signalvaultEnvironment,
|
|
305
|
+
provider: 'openai',
|
|
306
|
+
model: params.model,
|
|
307
|
+
metadata: {},
|
|
308
|
+
payload: { messages: params.messages, monitor_mode: true },
|
|
309
|
+
}),
|
|
310
|
+
});
|
|
311
|
+
// Send ai.response event
|
|
312
|
+
await fetch(`${this.signalvaultBaseUrl}/v1/events`, {
|
|
313
|
+
method: 'POST',
|
|
314
|
+
headers: {
|
|
315
|
+
'Content-Type': 'application/json',
|
|
316
|
+
Authorization: `Bearer ${this.signalvaultApiKey}`,
|
|
317
|
+
},
|
|
318
|
+
body: JSON.stringify({
|
|
319
|
+
type: 'ai.response',
|
|
320
|
+
request_id: requestId,
|
|
321
|
+
environment: this.signalvaultEnvironment,
|
|
322
|
+
provider: 'openai',
|
|
323
|
+
model: params.model,
|
|
324
|
+
metadata: {},
|
|
325
|
+
payload: {
|
|
326
|
+
output,
|
|
327
|
+
usage: { prompt_tokens: promptTokens, completion_tokens: completionTokens },
|
|
328
|
+
monitor_mode: true,
|
|
329
|
+
},
|
|
330
|
+
}),
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
exports.SignalVaultClient = SignalVaultClient;
|
|
335
|
+
exports.default = SignalVaultClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@signalvaultio/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SignalVault SDK - AI audit logs and guardrails for safe production AI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"test": "jest --passWithNoTests"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"audit",
|
|
19
|
+
"guardrails",
|
|
20
|
+
"openai",
|
|
21
|
+
"llm",
|
|
22
|
+
"compliance",
|
|
23
|
+
"security",
|
|
24
|
+
"monitoring"
|
|
25
|
+
],
|
|
26
|
+
"author": "SignalVault <support@signalvault.io>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/signalvaultio/signalvault-node"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/signalvaultio/signalvault-node/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://signalvault.io/docs",
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"openai": "^4.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"uuid": "^9.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/jest": "^30.0.0",
|
|
44
|
+
"@types/node": "^20.0.0",
|
|
45
|
+
"@types/uuid": "^9.0.0",
|
|
46
|
+
"jest": "^30.2.0",
|
|
47
|
+
"openai": "^4.0.0",
|
|
48
|
+
"ts-jest": "^29.4.6",
|
|
49
|
+
"typescript": "^5.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|