@tehreet/conduit
Version:
LLM API gateway with intelligent routing, robust process management, and health monitoring
243 lines • 9.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SynapsePlugin = void 0;
const log_1 = require("../utils/log");
class SynapsePlugin {
constructor() {
this.name = 'synapse-plugin';
this.version = '1.0.0';
}
async customRouter(context) {
(0, log_1.log)(`[SynapsePlugin] customRouter called with context:`, {
hasRequest: !!context.request,
hasRequestBody: !!context.requestBody,
hasSynapseContext: !!context.synapseContext,
requestUrl: context.request?.url
});
// First try to extract from HTTP request body
let synapseContextData = this.extractSynapseContextFromRequest(context);
// Fallback to environment variables if not in request
if (!synapseContextData) {
synapseContextData = this.extractSynapseContextData(context.env);
}
if (!synapseContextData) {
(0, log_1.log)(`[SynapsePlugin] No synapseContext found, returning null`);
return null;
}
const { projectConfig, agentConfig, routingRules } = synapseContextData;
// Evaluate routing rules in order
if (routingRules) {
for (const rule of routingRules) {
if (this.evaluateRule(rule, context)) {
(0, log_1.log)(`Synapse routing rule '${rule.name}' matched, using model: ${rule.targetModel}`);
return {
model: rule.targetModel,
source: 'synapse-plugin',
reason: `Synapse routing rule '${rule.name}' matched`,
tokenCount: context.tokenCount,
metadata: {
ruleName: rule.name,
ruleCondition: rule.condition
}
};
}
}
}
// Return configured model
if (agentConfig &&
!agentConfig.useProjectDefaults &&
agentConfig.overrideModel) {
(0, log_1.log)(`Using Synapse agent override model: ${agentConfig.overrideModel}`);
return {
model: agentConfig.overrideModel,
source: 'synapse-plugin',
reason: 'Synapse agent override model',
tokenCount: context.tokenCount,
metadata: {
configType: 'agent-override'
}
};
}
if (projectConfig && projectConfig.enabled && projectConfig.defaultModel) {
(0, log_1.log)(`Using Synapse project default model: ${projectConfig.defaultModel}`);
return {
model: projectConfig.defaultModel,
source: 'synapse-plugin',
reason: 'Synapse project default model',
tokenCount: context.tokenCount,
metadata: {
configType: 'project-default'
}
};
}
return null; // Let Conduit handle default routing
}
async onModelSelected(model, context) {
// Send telemetry back to Synapse
const telemetryEndpoint = context.env?.SYNAPSE_TELEMETRY_ENDPOINT;
if (telemetryEndpoint) {
try {
const response = await fetch(telemetryEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
event: 'model_selected',
model,
projectId: context.env.SYNAPSE_PROJECT_ID,
agentId: context.env.SYNAPSE_AGENT_ID,
timestamp: new Date().toISOString(),
}),
});
if (!response.ok) {
(0, log_1.log)(`Synapse telemetry failed: ${response.status} ${response.statusText}`);
}
}
catch (error) {
// Don't fail on telemetry errors, just log them
(0, log_1.log)(`Synapse telemetry error:`, error);
}
}
}
extractSynapseContextFromRequest(context) {
// Check multiple possible locations for synapseContext
let synapseContext = null;
// Try requestBody first (new field)
if (context.requestBody?.synapseContext) {
synapseContext = context.requestBody.synapseContext;
(0, log_1.log)('Found synapseContext in requestBody');
}
// Then try request.body
else if (context.request?.body?.synapseContext) {
synapseContext = context.request.body.synapseContext;
(0, log_1.log)('Found synapseContext in request.body');
}
// Try direct context
else if (context.synapseContext) {
synapseContext = context.synapseContext;
(0, log_1.log)('Found synapseContext in context root');
}
if (!synapseContext) {
(0, log_1.log)('No synapseContext found in request');
return null;
}
try {
// Handle both string and object formats
const data = typeof synapseContext === 'string'
? JSON.parse(synapseContext)
: synapseContext;
(0, log_1.log)('Parsed synapseContext:', data);
return data;
}
catch (error) {
(0, log_1.log)('Failed to parse synapseContext:', error);
return null;
}
}
extractSynapseContextData(env) {
// Check if we have a SYNAPSE_CONTEXT environment variable
if (env.SYNAPSE_CONTEXT) {
try {
return JSON.parse(env.SYNAPSE_CONTEXT);
}
catch (error) {
(0, log_1.log)('Error parsing SYNAPSE_CONTEXT:', error);
}
}
// Fallback to individual environment variables
const projectConfig = env.SYNAPSE_PROJECT_MODEL_CONFIG
? this.parseJsonSafely(env.SYNAPSE_PROJECT_MODEL_CONFIG)
: undefined;
const agentConfig = env.SYNAPSE_AGENT_MODEL_CONFIG
? this.parseJsonSafely(env.SYNAPSE_AGENT_MODEL_CONFIG)
: undefined;
if (projectConfig || agentConfig) {
return { projectConfig, agentConfig };
}
return null;
}
parseJsonSafely(jsonString) {
try {
return JSON.parse(jsonString);
}
catch (error) {
(0, log_1.log)('Error parsing JSON:', error);
return undefined;
}
}
evaluateRule(rule, context) {
try {
// Simple condition evaluation - can be extended for more complex rules
const condition = rule.condition.toLowerCase();
// Token count conditions
if (condition.includes('tokencount')) {
return this.evaluateTokenCountCondition(condition, context.tokenCount);
}
// Flags conditions
if (condition.includes('flags.includes')) {
return this.evaluateFlagsCondition(condition, context);
}
// Model type conditions
if (condition.includes('model')) {
return this.evaluateModelCondition(condition, context);
}
(0, log_1.log)(`Unknown condition in routing rule: ${condition}`);
return false;
}
catch (error) {
(0, log_1.log)(`Error evaluating routing rule '${rule.name}':`, error);
return false;
}
}
evaluateTokenCountCondition(condition, tokenCount) {
// Extract number from condition like "tokenCount < 4000"
const match = condition.match(/tokencount\s*([<>]=?)\s*(\d+)/);
if (!match)
return false;
const operator = match[1];
const threshold = parseInt(match[2]);
switch (operator) {
case '<':
return tokenCount < threshold;
case '<=':
return tokenCount <= threshold;
case '>':
return tokenCount > threshold;
case '>=':
return tokenCount >= threshold;
default:
return false;
}
}
evaluateFlagsCondition(condition, context) {
// Extract flag from condition like "flags.includes('--thinking')"
const match = condition.match(/flags\.includes\(['"]([^'"]+)['"]\)/);
if (!match)
return false;
const flag = match[1];
// Check if the flag exists in request body or other context
if (context.request?.body?.thinking && flag === '--thinking') {
return true;
}
// Could extend this to check other flags or request properties
return false;
}
evaluateModelCondition(condition, context) {
// Simple model matching - can be extended
const requestedModel = context.request?.body?.model || '';
if (condition.includes('haiku') && requestedModel.includes('haiku')) {
return true;
}
if (condition.includes('sonnet') && requestedModel.includes('sonnet')) {
return true;
}
if (condition.includes('opus') && requestedModel.includes('opus')) {
return true;
}
return false;
}
}
exports.SynapsePlugin = SynapsePlugin;
exports.default = SynapsePlugin;
//# sourceMappingURL=synapse-plugin.js.map