govyn 0.0.1 → 0.2.5
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 +263 -1
- package/configs/multi-provider.yaml +68 -0
- package/configs/openai-only.yaml +45 -0
- package/configs/team-setup.yaml +88 -0
- package/dist/action-logger.d.ts +128 -0
- package/dist/action-logger.js +356 -0
- package/dist/action-logger.js.map +1 -0
- package/dist/admin-cli.d.ts +2 -0
- package/dist/admin-cli.js +36 -0
- package/dist/admin-cli.js.map +1 -0
- package/dist/agents.d.ts +23 -0
- package/dist/agents.js +59 -0
- package/dist/agents.js.map +1 -0
- package/dist/alert-api.d.ts +14 -0
- package/dist/alert-api.js +355 -0
- package/dist/alert-api.js.map +1 -0
- package/dist/alert-manager.d.ts +77 -0
- package/dist/alert-manager.js +267 -0
- package/dist/alert-manager.js.map +1 -0
- package/dist/approval-api.d.ts +19 -0
- package/dist/approval-api.js +82 -0
- package/dist/approval-api.js.map +1 -0
- package/dist/approval-timeout.d.ts +29 -0
- package/dist/approval-timeout.js +45 -0
- package/dist/approval-timeout.js.map +1 -0
- package/dist/approval.d.ts +78 -0
- package/dist/approval.js +101 -0
- package/dist/approval.js.map +1 -0
- package/dist/auth.d.ts +47 -0
- package/dist/auth.js +335 -0
- package/dist/auth.js.map +1 -0
- package/dist/budget-api.d.ts +20 -0
- package/dist/budget-api.js +85 -0
- package/dist/budget-api.js.map +1 -0
- package/dist/budget-enforcer.d.ts +102 -0
- package/dist/budget-enforcer.js +294 -0
- package/dist/budget-enforcer.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +200 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +267 -0
- package/dist/config.js.map +1 -0
- package/dist/cost-aggregator.d.ts +69 -0
- package/dist/cost-aggregator.js +305 -0
- package/dist/cost-aggregator.js.map +1 -0
- package/dist/cost-api.d.ts +29 -0
- package/dist/cost-api.js +128 -0
- package/dist/cost-api.js.map +1 -0
- package/dist/database-url.d.ts +6 -0
- package/dist/database-url.js +47 -0
- package/dist/database-url.js.map +1 -0
- package/dist/db-retention.d.ts +53 -0
- package/dist/db-retention.js +82 -0
- package/dist/db-retention.js.map +1 -0
- package/dist/db-schema.d.ts +17 -0
- package/dist/db-schema.js +167 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/db-writer.d.ts +55 -0
- package/dist/db-writer.js +115 -0
- package/dist/db-writer.js.map +1 -0
- package/dist/db.d.ts +33 -0
- package/dist/db.js +78 -0
- package/dist/db.js.map +1 -0
- package/dist/events.d.ts +77 -0
- package/dist/events.js +12 -0
- package/dist/events.js.map +1 -0
- package/dist/health.d.ts +14 -0
- package/dist/health.js +49 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/init-wizard.d.ts +12 -0
- package/dist/init-wizard.js +206 -0
- package/dist/init-wizard.js.map +1 -0
- package/dist/log-api.d.ts +20 -0
- package/dist/log-api.js +371 -0
- package/dist/log-api.js.map +1 -0
- package/dist/log-rotator.d.ts +55 -0
- package/dist/log-rotator.js +157 -0
- package/dist/log-rotator.js.map +1 -0
- package/dist/loop-detector.d.ts +71 -0
- package/dist/loop-detector.js +122 -0
- package/dist/loop-detector.js.map +1 -0
- package/dist/persistence-types.d.ts +165 -0
- package/dist/persistence-types.js +2 -0
- package/dist/persistence-types.js.map +1 -0
- package/dist/persistence.d.ts +185 -0
- package/dist/persistence.js +785 -0
- package/dist/persistence.js.map +1 -0
- package/dist/policy-api.d.ts +25 -0
- package/dist/policy-api.js +347 -0
- package/dist/policy-api.js.map +1 -0
- package/dist/policy-engine.d.ts +76 -0
- package/dist/policy-engine.js +835 -0
- package/dist/policy-engine.js.map +1 -0
- package/dist/policy-file.d.ts +10 -0
- package/dist/policy-file.js +52 -0
- package/dist/policy-file.js.map +1 -0
- package/dist/policy-parser.d.ts +21 -0
- package/dist/policy-parser.js +560 -0
- package/dist/policy-parser.js.map +1 -0
- package/dist/policy-types.d.ts +216 -0
- package/dist/policy-types.js +8 -0
- package/dist/policy-types.js.map +1 -0
- package/dist/policy-watcher.d.ts +54 -0
- package/dist/policy-watcher.js +116 -0
- package/dist/policy-watcher.js.map +1 -0
- package/dist/pricing.d.ts +69 -0
- package/dist/pricing.js +93 -0
- package/dist/pricing.js.map +1 -0
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +47 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers/anthropic.d.ts +18 -0
- package/dist/providers/anthropic.js +61 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/custom.d.ts +19 -0
- package/dist/providers/custom.js +54 -0
- package/dist/providers/custom.js.map +1 -0
- package/dist/providers/openai.d.ts +17 -0
- package/dist/providers/openai.js +48 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/proxy.d.ts +57 -0
- package/dist/proxy.js +477 -0
- package/dist/proxy.js.map +1 -0
- package/dist/router.d.ts +23 -0
- package/dist/router.js +89 -0
- package/dist/router.js.map +1 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +139 -0
- package/dist/runtime.js.map +1 -0
- package/dist/security.d.ts +64 -0
- package/dist/security.js +422 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.js +1147 -0
- package/dist/server.js.map +1 -0
- package/dist/sqlite-schema.d.ts +6 -0
- package/dist/sqlite-schema.js +134 -0
- package/dist/sqlite-schema.js.map +1 -0
- package/dist/streaming.d.ts +24 -0
- package/dist/streaming.js +63 -0
- package/dist/streaming.js.map +1 -0
- package/dist/tokens.d.ts +45 -0
- package/dist/tokens.js +237 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +344 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -2
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request forwarding logic for the Govyn proxy server.
|
|
3
|
+
*
|
|
4
|
+
* Uses Node.js built-in http/https module for zero-dependency,
|
|
5
|
+
* low-latency request forwarding (per ADR-013).
|
|
6
|
+
*
|
|
7
|
+
* - Forwards ALL upstream response headers verbatim, including rate-limit headers
|
|
8
|
+
* (Retry-After, x-ratelimit-*) — per ADR-016: 429s are passed through, not retried
|
|
9
|
+
* - Delegates SSE responses to handleStreamingResponse for chunk-by-chunk piping
|
|
10
|
+
* - Returns 502 only for proxy-own errors (upstream unreachable, connection timeout)
|
|
11
|
+
* - After each response completes, extracts tokens and records cost (non-blocking)
|
|
12
|
+
*/
|
|
13
|
+
import * as http from 'node:http';
|
|
14
|
+
import * as https from 'node:https';
|
|
15
|
+
import * as crypto from 'node:crypto';
|
|
16
|
+
import { mapOpenAIHeaders } from './providers/openai.js';
|
|
17
|
+
import { mapAnthropicHeaders } from './providers/anthropic.js';
|
|
18
|
+
import { mapCustomHeaders } from './providers/custom.js';
|
|
19
|
+
import { handleStreamingResponse } from './streaming.js';
|
|
20
|
+
import { extractTokenUsage, extractTokenUsageFromSSE } from './tokens.js';
|
|
21
|
+
import { calculateCost } from './pricing.js';
|
|
22
|
+
import { govynEvents } from './events.js';
|
|
23
|
+
const HOP_BY_HOP_RESPONSE_HEADERS = new Set([
|
|
24
|
+
'connection',
|
|
25
|
+
'keep-alive',
|
|
26
|
+
'proxy-authenticate',
|
|
27
|
+
'proxy-authorization',
|
|
28
|
+
'te',
|
|
29
|
+
'trailer',
|
|
30
|
+
'transfer-encoding',
|
|
31
|
+
'upgrade',
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Select the appropriate header mapping function based on provider type.
|
|
35
|
+
*/
|
|
36
|
+
function mapHeaders(incomingHeaders, routeMatch) {
|
|
37
|
+
const { provider, providerType } = routeMatch;
|
|
38
|
+
switch (providerType) {
|
|
39
|
+
case 'openai':
|
|
40
|
+
return mapOpenAIHeaders(incomingHeaders, provider.apiKeyEnv);
|
|
41
|
+
case 'anthropic':
|
|
42
|
+
return mapAnthropicHeaders(incomingHeaders, provider.apiKeyEnv);
|
|
43
|
+
case 'custom':
|
|
44
|
+
return mapCustomHeaders(incomingHeaders, provider.apiKeyEnv);
|
|
45
|
+
default: {
|
|
46
|
+
// Exhaustive check
|
|
47
|
+
const _exhaustive = providerType;
|
|
48
|
+
throw new Error(`Unknown provider type: ${String(_exhaustive)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Send a JSON error response to the client.
|
|
54
|
+
* Only used for proxy-own errors (upstream unreachable, timeout, etc.).
|
|
55
|
+
* Upstream error responses (4xx, 5xx) are forwarded verbatim.
|
|
56
|
+
*/
|
|
57
|
+
function sendErrorResponse(res, statusCode, message, code) {
|
|
58
|
+
const body = JSON.stringify({ error: { message, code } });
|
|
59
|
+
if (!res.headersSent) {
|
|
60
|
+
res.writeHead(statusCode, {
|
|
61
|
+
'content-type': 'application/json',
|
|
62
|
+
'content-length': Buffer.byteLength(body).toString(),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
res.end(body);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Send a loop_detected 429 response in Govyn-native format.
|
|
69
|
+
*
|
|
70
|
+
* @param res - The outgoing client response
|
|
71
|
+
* @param agentId - The agent that triggered loop detection
|
|
72
|
+
* @param cooldownSeconds - How long the agent will be blocked
|
|
73
|
+
*/
|
|
74
|
+
function sendLoopDetectedError(res, agentId, cooldownSeconds) {
|
|
75
|
+
const cooldownExpiresAt = new Date(Date.now() + cooldownSeconds * 1000).toISOString();
|
|
76
|
+
const body = JSON.stringify({
|
|
77
|
+
error: {
|
|
78
|
+
type: 'loop_error',
|
|
79
|
+
code: 'loop_detected',
|
|
80
|
+
message: 'Agent blocked: repeated identical requests detected',
|
|
81
|
+
details: {
|
|
82
|
+
agent_id: agentId,
|
|
83
|
+
cooldown_seconds: cooldownSeconds,
|
|
84
|
+
cooldown_expires_at: cooldownExpiresAt,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
res.writeHead(429, {
|
|
89
|
+
'content-type': 'application/json',
|
|
90
|
+
'content-length': Buffer.byteLength(body).toString(),
|
|
91
|
+
'retry-after': cooldownSeconds.toString(),
|
|
92
|
+
});
|
|
93
|
+
res.end(body);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Forward an incoming HTTP request to the upstream provider API.
|
|
97
|
+
*
|
|
98
|
+
* - Uses Node.js http/https module (NOT node-fetch, NOT axios)
|
|
99
|
+
* - Reads request body from incoming stream
|
|
100
|
+
* - Maps headers for the target provider
|
|
101
|
+
* - Forwards ALL upstream response headers verbatim (including rate-limit headers)
|
|
102
|
+
* - For SSE responses: delegates to handleStreamingResponse for chunk-by-chunk piping
|
|
103
|
+
* - For non-SSE responses: pipes upstream response body to client directly
|
|
104
|
+
* - For upstream errors (4xx, 5xx): forwards status code + headers + body verbatim
|
|
105
|
+
* - For proxy errors (upstream unreachable, timeout): returns 502 with Govyn error format
|
|
106
|
+
* - Logs time from request start to first upstream byte
|
|
107
|
+
* - After response completes, extracts token usage and records cost (non-blocking)
|
|
108
|
+
* - If loopDetector provided: checks for repeated identical requests before forwarding
|
|
109
|
+
*
|
|
110
|
+
* @param req - The incoming client request
|
|
111
|
+
* @param res - The outgoing client response
|
|
112
|
+
* @param routeMatch - The matched route (provider, path, type)
|
|
113
|
+
* @param agentId - The resolved agent identifier for this request
|
|
114
|
+
* @param pricingTable - Pricing table for cost calculation
|
|
115
|
+
* @param aggregator - Cost aggregator to record results
|
|
116
|
+
* @param budgetWarning - Optional budget warning info to add as response header
|
|
117
|
+
* @param loopDetector - Optional loop detector for detecting repeated identical requests
|
|
118
|
+
* @param budgetEnforcer - Optional budget enforcer for triggering loop block on detection
|
|
119
|
+
* @param actionLogger - Optional action logger for structured request logging
|
|
120
|
+
*/
|
|
121
|
+
export async function forwardRequest(req, res, routeMatch, agentId, pricingTable, aggregator, budgetWarning, loopDetector, budgetEnforcer, actionLogger, bufferedBody, requestedModel, policyResult, dbWriter) {
|
|
122
|
+
const requestStart = Date.now();
|
|
123
|
+
const { provider, upstreamPath } = routeMatch;
|
|
124
|
+
// Parse the upstream base URL
|
|
125
|
+
let upstreamUrl;
|
|
126
|
+
try {
|
|
127
|
+
upstreamUrl = new URL(provider.baseUrl);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
sendErrorResponse(res, 502, 'Invalid upstream base URL configured', 'invalid_config');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Build upstream request options
|
|
134
|
+
const upstreamHost = upstreamUrl.hostname;
|
|
135
|
+
const upstreamPort = upstreamUrl.port
|
|
136
|
+
? parseInt(upstreamUrl.port, 10)
|
|
137
|
+
: upstreamUrl.protocol === 'https:'
|
|
138
|
+
? 443
|
|
139
|
+
: 80;
|
|
140
|
+
const isHttps = upstreamUrl.protocol === 'https:';
|
|
141
|
+
// Map headers for the upstream provider
|
|
142
|
+
const mappedHeaders = mapHeaders(req.headers, routeMatch);
|
|
143
|
+
// Use pre-buffered body if provided (from server.ts policy evaluation flow),
|
|
144
|
+
// otherwise read from the request stream directly (backward compat)
|
|
145
|
+
let body;
|
|
146
|
+
if (bufferedBody !== undefined) {
|
|
147
|
+
body = bufferedBody;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const bodyChunks = [];
|
|
151
|
+
try {
|
|
152
|
+
await new Promise((resolve, reject) => {
|
|
153
|
+
req.on('data', (chunk) => bodyChunks.push(chunk));
|
|
154
|
+
req.on('end', resolve);
|
|
155
|
+
req.on('error', reject);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
sendErrorResponse(res, 500, 'Error reading request body', 'request_read_error');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
body = Buffer.concat(bodyChunks);
|
|
163
|
+
}
|
|
164
|
+
// Loop detection: check for repeated identical requests before forwarding
|
|
165
|
+
if (loopDetector && budgetEnforcer) {
|
|
166
|
+
const bodyHash = loopDetector.getRequestHash(body);
|
|
167
|
+
loopDetector.recordRequest(agentId, routeMatch.upstreamPath, bodyHash);
|
|
168
|
+
if (loopDetector.isLooping(agentId, routeMatch.upstreamPath, bodyHash)) {
|
|
169
|
+
// Get agent-specific cooldown (or default 300s)
|
|
170
|
+
const agentLoopConfig = loopDetector.getAgentConfig(agentId);
|
|
171
|
+
const cooldownSeconds = agentLoopConfig.cooldownSeconds;
|
|
172
|
+
budgetEnforcer.blockAgent(agentId, 'loop_detected', cooldownSeconds);
|
|
173
|
+
console.warn(`[govyn] Loop detected: agent=${agentId} path=${routeMatch.upstreamPath} bodyHash=${bodyHash} cooldown=${cooldownSeconds}s`);
|
|
174
|
+
govynEvents.emit('event', {
|
|
175
|
+
type: 'loop_detected',
|
|
176
|
+
agentId,
|
|
177
|
+
cooldownSeconds,
|
|
178
|
+
});
|
|
179
|
+
// Log the loop_detected event
|
|
180
|
+
if (actionLogger) {
|
|
181
|
+
const logEntry = {
|
|
182
|
+
id: crypto.randomUUID(),
|
|
183
|
+
timestamp: new Date().toISOString(),
|
|
184
|
+
agent_id: agentId,
|
|
185
|
+
provider: routeMatch.providerType,
|
|
186
|
+
target: routeMatch.upstreamPath,
|
|
187
|
+
model: null,
|
|
188
|
+
input_tokens: null,
|
|
189
|
+
output_tokens: null,
|
|
190
|
+
cost: null,
|
|
191
|
+
priced: false,
|
|
192
|
+
latency_ms: Date.now() - requestStart,
|
|
193
|
+
status: 429,
|
|
194
|
+
has_payload: false,
|
|
195
|
+
payload_id: null,
|
|
196
|
+
storage_region: 'auto',
|
|
197
|
+
};
|
|
198
|
+
actionLogger.log(logEntry);
|
|
199
|
+
}
|
|
200
|
+
sendLoopDetectedError(res, agentId, cooldownSeconds);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Update content-length to match actual body
|
|
205
|
+
if (body.length > 0) {
|
|
206
|
+
mappedHeaders['content-length'] = body.length.toString();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
delete mappedHeaders['content-length'];
|
|
210
|
+
}
|
|
211
|
+
const requestOptions = {
|
|
212
|
+
hostname: upstreamHost,
|
|
213
|
+
port: upstreamPort,
|
|
214
|
+
path: upstreamPath,
|
|
215
|
+
method: req.method ?? 'GET',
|
|
216
|
+
headers: mappedHeaders,
|
|
217
|
+
};
|
|
218
|
+
// Make the upstream request
|
|
219
|
+
return new Promise((resolve) => {
|
|
220
|
+
const transport = isHttps ? https : http;
|
|
221
|
+
const upstreamReq = transport.request(requestOptions, (upstreamRes) => {
|
|
222
|
+
const firstByteTime = Date.now();
|
|
223
|
+
const latency = firstByteTime - requestStart;
|
|
224
|
+
const statusCode = upstreamRes.statusCode ?? 200;
|
|
225
|
+
console.log(`[proxy] ${req.method} ${upstreamPath} -> ${provider.baseUrl} | status=${statusCode} | latency=${latency}ms`);
|
|
226
|
+
// Build response headers — forward ALL upstream headers verbatim.
|
|
227
|
+
// This is critical for rate-limit headers (Retry-After, x-ratelimit-*) per ADR-016.
|
|
228
|
+
// Upstream errors (4xx, 5xx) are passed through without modification.
|
|
229
|
+
const responseHeaders = {};
|
|
230
|
+
for (const [key, value] of Object.entries(upstreamRes.headers)) {
|
|
231
|
+
if (value !== undefined && !HOP_BY_HOP_RESPONSE_HEADERS.has(key.toLowerCase())) {
|
|
232
|
+
responseHeaders[key] = value;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Build budget warning header value if applicable
|
|
236
|
+
const budgetWarningHeaderValue = budgetWarning
|
|
237
|
+
? JSON.stringify({
|
|
238
|
+
percent_used: budgetWarning.percentUsed,
|
|
239
|
+
current_spend: budgetWarning.currentSpend,
|
|
240
|
+
limit: budgetWarning.limit,
|
|
241
|
+
resets_at: budgetWarning.resetsAt,
|
|
242
|
+
})
|
|
243
|
+
: undefined;
|
|
244
|
+
// Check if the upstream response is SSE (text/event-stream)
|
|
245
|
+
const contentType = upstreamRes.headers['content-type'] ?? '';
|
|
246
|
+
const isSSE = contentType.includes('text/event-stream');
|
|
247
|
+
if (isSSE) {
|
|
248
|
+
// Accumulate SSE chunks for token extraction (concurrent with piping to client)
|
|
249
|
+
const sseChunks = [];
|
|
250
|
+
upstreamRes.on('data', (chunk) => {
|
|
251
|
+
sseChunks.push(chunk.toString('utf8'));
|
|
252
|
+
});
|
|
253
|
+
// Build extra headers for SSE response (budget warning if applicable)
|
|
254
|
+
const sseExtraHeaders = budgetWarningHeaderValue
|
|
255
|
+
? { 'x-govyn-budget-warning': budgetWarningHeaderValue }
|
|
256
|
+
: undefined;
|
|
257
|
+
// Delegate to streaming handler — sets its own headers (content-type, cache-control, connection)
|
|
258
|
+
// and pipes chunks without buffering
|
|
259
|
+
handleStreamingResponse(upstreamRes, res, statusCode, sseExtraHeaders);
|
|
260
|
+
// After the stream ends, extract tokens and record cost
|
|
261
|
+
// (happens after all data has been piped to client)
|
|
262
|
+
upstreamRes.on('end', () => {
|
|
263
|
+
const usage = extractTokenUsageFromSSE(sseChunks, routeMatch.providerType);
|
|
264
|
+
let costResult;
|
|
265
|
+
if (usage) {
|
|
266
|
+
costResult = calculateCost(usage, pricingTable);
|
|
267
|
+
const costRecord = {
|
|
268
|
+
agentId,
|
|
269
|
+
model: usage.model,
|
|
270
|
+
provider: routeMatch.providerType,
|
|
271
|
+
inputTokens: usage.inputTokens,
|
|
272
|
+
outputTokens: usage.outputTokens,
|
|
273
|
+
inputCost: costResult.inputCost,
|
|
274
|
+
outputCost: costResult.outputCost,
|
|
275
|
+
totalCost: costResult.totalCost,
|
|
276
|
+
priced: costResult.priced,
|
|
277
|
+
timestamp: Date.now(),
|
|
278
|
+
requestedModel: requestedModel ?? undefined,
|
|
279
|
+
};
|
|
280
|
+
aggregator.recordCost(costRecord);
|
|
281
|
+
// DB persistence (fire-and-forget, parallel with in-memory + JSONL)
|
|
282
|
+
dbWriter?.writeCostRecord(costRecord).catch(() => { });
|
|
283
|
+
const totalTokens = usage.inputTokens + usage.outputTokens;
|
|
284
|
+
console.log(`[govyn] Cost: agent=${agentId} model=${usage.model} tokens=${totalTokens} cost=$${costResult.totalCost.toFixed(6)} priced=${costResult.priced}`);
|
|
285
|
+
}
|
|
286
|
+
// Action logging (SSE path)
|
|
287
|
+
if (actionLogger) {
|
|
288
|
+
const mode = actionLogger.getMode(agentId);
|
|
289
|
+
const logId = crypto.randomUUID();
|
|
290
|
+
const payloadId = mode === 'full-payload' ? crypto.randomUUID() : null;
|
|
291
|
+
const logEntry = {
|
|
292
|
+
id: logId,
|
|
293
|
+
timestamp: new Date().toISOString(),
|
|
294
|
+
agent_id: agentId,
|
|
295
|
+
provider: routeMatch.providerType,
|
|
296
|
+
target: routeMatch.upstreamPath,
|
|
297
|
+
model: usage?.model ?? null,
|
|
298
|
+
input_tokens: usage?.inputTokens ?? null,
|
|
299
|
+
output_tokens: usage?.outputTokens ?? null,
|
|
300
|
+
cost: costResult?.totalCost ?? null,
|
|
301
|
+
priced: costResult?.priced ?? false,
|
|
302
|
+
latency_ms: Date.now() - requestStart,
|
|
303
|
+
status: statusCode,
|
|
304
|
+
has_payload: payloadId !== null,
|
|
305
|
+
payload_id: payloadId,
|
|
306
|
+
storage_region: 'auto',
|
|
307
|
+
requested_model: requestedModel ?? null,
|
|
308
|
+
actual_model: usage?.model ?? null,
|
|
309
|
+
policy_result: policyResult ? {
|
|
310
|
+
allowed: policyResult.allowed,
|
|
311
|
+
evaluated_count: policyResult.evaluatedCount,
|
|
312
|
+
matched_count: policyResult.matchedCount,
|
|
313
|
+
evaluation_time_ms: policyResult.evaluationTimeMs,
|
|
314
|
+
} : undefined,
|
|
315
|
+
};
|
|
316
|
+
actionLogger.log(logEntry);
|
|
317
|
+
if (payloadId) {
|
|
318
|
+
const sseResBody = Buffer.from(sseChunks.join(''), 'utf8');
|
|
319
|
+
const maxSize = actionLogger.config.maxBodySize;
|
|
320
|
+
const truncated = (body.length > maxSize) || (sseResBody.length > maxSize);
|
|
321
|
+
actionLogger.storePayload(payloadId, body, sseResBody, truncated);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
// Resolve when the pipe ends (client close or upstream end)
|
|
326
|
+
res.on('finish', resolve);
|
|
327
|
+
res.on('close', resolve);
|
|
328
|
+
upstreamRes.on('error', resolve);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// Non-streaming: forward status + all headers + body verbatim
|
|
332
|
+
// Add budget warning header if applicable
|
|
333
|
+
if (budgetWarningHeaderValue) {
|
|
334
|
+
responseHeaders['x-govyn-budget-warning'] = budgetWarningHeaderValue;
|
|
335
|
+
}
|
|
336
|
+
res.writeHead(statusCode, responseHeaders);
|
|
337
|
+
upstreamRes.pipe(res);
|
|
338
|
+
// Accumulate body for token extraction (concurrent with piping to client)
|
|
339
|
+
const responseBodyChunks = [];
|
|
340
|
+
upstreamRes.on('data', (chunk) => {
|
|
341
|
+
responseBodyChunks.push(chunk);
|
|
342
|
+
});
|
|
343
|
+
upstreamRes.on('end', () => {
|
|
344
|
+
// Extract tokens from the buffered response body
|
|
345
|
+
const responseBody = Buffer.concat(responseBodyChunks);
|
|
346
|
+
const responseBodyStr = responseBody.toString('utf8');
|
|
347
|
+
const usage = extractTokenUsage(responseBodyStr, routeMatch.providerType);
|
|
348
|
+
let costResult;
|
|
349
|
+
if (usage) {
|
|
350
|
+
costResult = calculateCost(usage, pricingTable);
|
|
351
|
+
const costRecord = {
|
|
352
|
+
agentId,
|
|
353
|
+
model: usage.model,
|
|
354
|
+
provider: routeMatch.providerType,
|
|
355
|
+
inputTokens: usage.inputTokens,
|
|
356
|
+
outputTokens: usage.outputTokens,
|
|
357
|
+
inputCost: costResult.inputCost,
|
|
358
|
+
outputCost: costResult.outputCost,
|
|
359
|
+
totalCost: costResult.totalCost,
|
|
360
|
+
priced: costResult.priced,
|
|
361
|
+
timestamp: Date.now(),
|
|
362
|
+
requestedModel: requestedModel ?? undefined,
|
|
363
|
+
};
|
|
364
|
+
aggregator.recordCost(costRecord);
|
|
365
|
+
// DB persistence (fire-and-forget, parallel with in-memory + JSONL)
|
|
366
|
+
dbWriter?.writeCostRecord(costRecord).catch(() => { });
|
|
367
|
+
const totalTokens = usage.inputTokens + usage.outputTokens;
|
|
368
|
+
console.log(`[govyn] Cost: agent=${agentId} model=${usage.model} tokens=${totalTokens} cost=$${costResult.totalCost.toFixed(6)} priced=${costResult.priced}`);
|
|
369
|
+
}
|
|
370
|
+
// Action logging (non-SSE path)
|
|
371
|
+
if (actionLogger) {
|
|
372
|
+
const mode = actionLogger.getMode(agentId);
|
|
373
|
+
const logId = crypto.randomUUID();
|
|
374
|
+
const payloadId = mode === 'full-payload' ? crypto.randomUUID() : null;
|
|
375
|
+
const logEntry = {
|
|
376
|
+
id: logId,
|
|
377
|
+
timestamp: new Date().toISOString(),
|
|
378
|
+
agent_id: agentId,
|
|
379
|
+
provider: routeMatch.providerType,
|
|
380
|
+
target: routeMatch.upstreamPath,
|
|
381
|
+
model: usage?.model ?? null,
|
|
382
|
+
input_tokens: usage?.inputTokens ?? null,
|
|
383
|
+
output_tokens: usage?.outputTokens ?? null,
|
|
384
|
+
cost: costResult?.totalCost ?? null,
|
|
385
|
+
priced: costResult?.priced ?? false,
|
|
386
|
+
latency_ms: Date.now() - requestStart,
|
|
387
|
+
status: statusCode,
|
|
388
|
+
has_payload: payloadId !== null,
|
|
389
|
+
payload_id: payloadId,
|
|
390
|
+
storage_region: 'auto',
|
|
391
|
+
requested_model: requestedModel ?? null,
|
|
392
|
+
actual_model: usage?.model ?? null,
|
|
393
|
+
policy_result: policyResult ? {
|
|
394
|
+
allowed: policyResult.allowed,
|
|
395
|
+
evaluated_count: policyResult.evaluatedCount,
|
|
396
|
+
matched_count: policyResult.matchedCount,
|
|
397
|
+
evaluation_time_ms: policyResult.evaluationTimeMs,
|
|
398
|
+
} : undefined,
|
|
399
|
+
};
|
|
400
|
+
actionLogger.log(logEntry);
|
|
401
|
+
if (payloadId) {
|
|
402
|
+
const maxSize = actionLogger.config.maxBodySize;
|
|
403
|
+
const truncated = (body.length > maxSize) || (responseBody.length > maxSize);
|
|
404
|
+
actionLogger.storePayload(payloadId, body, responseBody, truncated);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
resolve();
|
|
408
|
+
});
|
|
409
|
+
upstreamRes.on('error', (err) => {
|
|
410
|
+
console.error('[proxy] upstream response error:', err.message);
|
|
411
|
+
if (!res.writableEnded) {
|
|
412
|
+
res.end();
|
|
413
|
+
}
|
|
414
|
+
resolve();
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
upstreamReq.on('error', (err) => {
|
|
419
|
+
console.error(`[proxy] upstream connection error: ${err.message}`);
|
|
420
|
+
// Log the connection error
|
|
421
|
+
if (actionLogger) {
|
|
422
|
+
const logEntry = {
|
|
423
|
+
id: crypto.randomUUID(),
|
|
424
|
+
timestamp: new Date().toISOString(),
|
|
425
|
+
agent_id: agentId,
|
|
426
|
+
provider: routeMatch.providerType,
|
|
427
|
+
target: routeMatch.upstreamPath,
|
|
428
|
+
model: null,
|
|
429
|
+
input_tokens: null,
|
|
430
|
+
output_tokens: null,
|
|
431
|
+
cost: null,
|
|
432
|
+
priced: false,
|
|
433
|
+
latency_ms: Date.now() - requestStart,
|
|
434
|
+
status: 502,
|
|
435
|
+
has_payload: false,
|
|
436
|
+
payload_id: null,
|
|
437
|
+
storage_region: 'auto',
|
|
438
|
+
};
|
|
439
|
+
actionLogger.log(logEntry);
|
|
440
|
+
}
|
|
441
|
+
sendErrorResponse(res, 502, 'Upstream connection failed', 'upstream_connection_error');
|
|
442
|
+
resolve();
|
|
443
|
+
});
|
|
444
|
+
upstreamReq.on('timeout', () => {
|
|
445
|
+
// Log the timeout error
|
|
446
|
+
if (actionLogger) {
|
|
447
|
+
const logEntry = {
|
|
448
|
+
id: crypto.randomUUID(),
|
|
449
|
+
timestamp: new Date().toISOString(),
|
|
450
|
+
agent_id: agentId,
|
|
451
|
+
provider: routeMatch.providerType,
|
|
452
|
+
target: routeMatch.upstreamPath,
|
|
453
|
+
model: null,
|
|
454
|
+
input_tokens: null,
|
|
455
|
+
output_tokens: null,
|
|
456
|
+
cost: null,
|
|
457
|
+
priced: false,
|
|
458
|
+
latency_ms: Date.now() - requestStart,
|
|
459
|
+
status: 502,
|
|
460
|
+
has_payload: false,
|
|
461
|
+
payload_id: null,
|
|
462
|
+
storage_region: 'auto',
|
|
463
|
+
};
|
|
464
|
+
actionLogger.log(logEntry);
|
|
465
|
+
}
|
|
466
|
+
upstreamReq.destroy();
|
|
467
|
+
sendErrorResponse(res, 502, 'Upstream request timed out', 'upstream_timeout');
|
|
468
|
+
resolve();
|
|
469
|
+
});
|
|
470
|
+
// Write request body to upstream
|
|
471
|
+
if (body.length > 0) {
|
|
472
|
+
upstreamReq.write(body);
|
|
473
|
+
}
|
|
474
|
+
upstreamReq.end();
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL routing module for the Govyn proxy server.
|
|
3
|
+
*
|
|
4
|
+
* Parses incoming request URLs and matches them to provider configurations.
|
|
5
|
+
* Supported patterns:
|
|
6
|
+
* /v1/openai/* -> OpenAI provider
|
|
7
|
+
* /v1/anthropic/* -> Anthropic provider
|
|
8
|
+
* /v1/custom/:name/* -> Custom provider by name
|
|
9
|
+
*/
|
|
10
|
+
import type { ProviderConfig, RouteMatch } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parse a request URL and match it against configured providers.
|
|
13
|
+
*
|
|
14
|
+
* @param url - The incoming request URL (path + query string)
|
|
15
|
+
* @param providers - Map of provider name to ProviderConfig
|
|
16
|
+
* @returns RouteMatch if a provider is found, null otherwise
|
|
17
|
+
*/
|
|
18
|
+
export declare function matchRoute(url: string, providers: Map<string, ProviderConfig>): RouteMatch | null;
|
|
19
|
+
/**
|
|
20
|
+
* Create a router function bound to a specific providers map.
|
|
21
|
+
* Returns a function that takes a URL and returns a RouteMatch or null.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createRouter(providers: Map<string, ProviderConfig>): (url: string) => RouteMatch | null;
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL routing module for the Govyn proxy server.
|
|
3
|
+
*
|
|
4
|
+
* Parses incoming request URLs and matches them to provider configurations.
|
|
5
|
+
* Supported patterns:
|
|
6
|
+
* /v1/openai/* -> OpenAI provider
|
|
7
|
+
* /v1/anthropic/* -> Anthropic provider
|
|
8
|
+
* /v1/custom/:name/* -> Custom provider by name
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Route prefixes for versioned API routing.
|
|
12
|
+
*/
|
|
13
|
+
const ROUTE_OPENAI = '/v1/openai';
|
|
14
|
+
const ROUTE_ANTHROPIC = '/v1/anthropic';
|
|
15
|
+
const ROUTE_CUSTOM_PREFIX = '/v1/custom/';
|
|
16
|
+
/**
|
|
17
|
+
* Parse a request URL and match it against configured providers.
|
|
18
|
+
*
|
|
19
|
+
* @param url - The incoming request URL (path + query string)
|
|
20
|
+
* @param providers - Map of provider name to ProviderConfig
|
|
21
|
+
* @returns RouteMatch if a provider is found, null otherwise
|
|
22
|
+
*/
|
|
23
|
+
export function matchRoute(url, providers) {
|
|
24
|
+
// Normalize: strip query string for routing, keep for upstream path
|
|
25
|
+
const questionMarkIdx = url.indexOf('?');
|
|
26
|
+
const path = questionMarkIdx >= 0 ? url.slice(0, questionMarkIdx) : url;
|
|
27
|
+
const queryString = questionMarkIdx >= 0 ? url.slice(questionMarkIdx) : '';
|
|
28
|
+
// Match /v1/openai/*
|
|
29
|
+
if (path.startsWith(ROUTE_OPENAI + '/') || path === ROUTE_OPENAI) {
|
|
30
|
+
const provider = providers.get('openai');
|
|
31
|
+
if (!provider)
|
|
32
|
+
return null;
|
|
33
|
+
// Strip /v1/openai prefix, keep the rest as upstream path
|
|
34
|
+
const upstreamPath = path.slice(ROUTE_OPENAI.length) + queryString;
|
|
35
|
+
return {
|
|
36
|
+
provider,
|
|
37
|
+
upstreamPath: upstreamPath || '/',
|
|
38
|
+
providerType: 'openai',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Match /v1/anthropic/*
|
|
42
|
+
if (path.startsWith(ROUTE_ANTHROPIC + '/') || path === ROUTE_ANTHROPIC) {
|
|
43
|
+
const provider = providers.get('anthropic');
|
|
44
|
+
if (!provider)
|
|
45
|
+
return null;
|
|
46
|
+
// Strip /v1/anthropic prefix, keep the rest as upstream path
|
|
47
|
+
const upstreamPath = path.slice(ROUTE_ANTHROPIC.length) + queryString;
|
|
48
|
+
return {
|
|
49
|
+
provider,
|
|
50
|
+
upstreamPath: upstreamPath || '/',
|
|
51
|
+
providerType: 'anthropic',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Match /v1/custom/:name/*
|
|
55
|
+
if (path.startsWith(ROUTE_CUSTOM_PREFIX)) {
|
|
56
|
+
const afterPrefix = path.slice(ROUTE_CUSTOM_PREFIX.length);
|
|
57
|
+
const slashIdx = afterPrefix.indexOf('/');
|
|
58
|
+
// Extract provider name and remaining path
|
|
59
|
+
let providerName;
|
|
60
|
+
let remainingPath;
|
|
61
|
+
if (slashIdx >= 0) {
|
|
62
|
+
providerName = afterPrefix.slice(0, slashIdx);
|
|
63
|
+
remainingPath = afterPrefix.slice(slashIdx) + queryString;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
providerName = afterPrefix;
|
|
67
|
+
remainingPath = '/' + queryString;
|
|
68
|
+
}
|
|
69
|
+
if (!providerName)
|
|
70
|
+
return null;
|
|
71
|
+
const provider = providers.get(providerName);
|
|
72
|
+
if (!provider)
|
|
73
|
+
return null;
|
|
74
|
+
return {
|
|
75
|
+
provider,
|
|
76
|
+
upstreamPath: remainingPath || '/',
|
|
77
|
+
providerType: 'custom',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a router function bound to a specific providers map.
|
|
84
|
+
* Returns a function that takes a URL and returns a RouteMatch or null.
|
|
85
|
+
*/
|
|
86
|
+
export function createRouter(providers) {
|
|
87
|
+
return (url) => matchRoute(url, providers);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,YAAY,GAAG,YAAY,CAAC;AAClC,MAAM,eAAe,GAAG,eAAe,CAAC;AACxC,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,GAAW,EACX,SAAsC;IAEtC,oEAAoE;IACpE,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,eAAe,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACxE,MAAM,WAAW,GAAG,eAAe,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3E,qBAAqB;IACrB,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,GAAG,GAAG,CAAC,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QACjE,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,0DAA0D;QAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;QAEnE,OAAO;YACL,QAAQ;YACR,YAAY,EAAE,YAAY,IAAI,GAAG;YACjC,YAAY,EAAE,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,wBAAwB;IACxB,IAAI,IAAI,CAAC,UAAU,CAAC,eAAe,GAAG,GAAG,CAAC,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QACvE,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,6DAA6D;QAC7D,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;QAEtE,OAAO;YACL,QAAQ;YACR,YAAY,EAAE,YAAY,IAAI,GAAG;YACjC,YAAY,EAAE,WAAW;SAC1B,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACzC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAE1C,2CAA2C;QAC3C,IAAI,YAAoB,CAAC;QACzB,IAAI,aAAqB,CAAC;QAE1B,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC9C,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,WAAW,CAAC;YAC3B,aAAa,GAAG,GAAG,GAAG,WAAW,CAAC;QACpC,CAAC;QAED,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAE/B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,OAAO;YACL,QAAQ;YACR,YAAY,EAAE,aAAa,IAAI,GAAG;YAClC,YAAY,EAAE,QAAQ;SACvB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAsC;IAEtC,OAAO,CAAC,GAAW,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startProxyRuntime(configPath?: string): Promise<void>;
|