@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
245 lines • 12.3 kB
JavaScript
/**
* Agent Routes
* Endpoints for agent execution and streaming
*/
import { SpanStatusCode } from "@opentelemetry/api";
import { ProviderFactory } from "../../factories/providerFactory.js";
import { withSpan } from "../../telemetry/withSpan.js";
import { tracers } from "../../telemetry/tracers.js";
import { createStreamRedactor } from "../utils/redaction.js";
import { AgentExecuteRequestSchema, createErrorResponse as createError, EmbedManyRequestSchema, EmbedRequestSchema, validateRequest, } from "../utils/validation.js";
/**
* Create agent routes
*/
export function createAgentRoutes(basePath = "/api") {
return {
prefix: `${basePath}/agent`,
routes: [
{
method: "POST",
path: `${basePath}/agent/execute`,
handler: async (ctx) => {
// Validate request body
const validation = validateRequest(AgentExecuteRequestSchema, ctx.body, ctx.requestId);
if (!validation.success) {
return validation.error;
}
const request = validation.data;
return withSpan({
name: "neurolink.http.execute",
tracer: tracers.http,
attributes: {
"http.route": "/api/agent/execute",
"ai.provider": request.provider || "default",
"ai.model": request.model || "default",
},
}, async () => {
// Normalize input
const input = typeof request.input === "string"
? { text: request.input }
: request.input;
const result = await ctx.neurolink.generate({
input,
provider: request.provider,
model: request.model,
systemPrompt: request.systemPrompt,
temperature: request.temperature,
maxTokens: request.maxTokens,
// Note: tools should be passed as Record<string, Tool> in generate options
// If request.tools is an array of tool names, we skip them
context: {
// When an authenticated user context exists (set by auth middleware),
// always use its IDs to prevent caller-supplied impersonation.
sessionId: ctx.user
? ctx.session?.id
: (ctx.session?.id ?? request.sessionId),
userId: ctx.user ? ctx.user.id : request.userId,
userEmail: ctx.user?.email,
userRoles: ctx.user?.roles,
requestId: ctx.requestId,
},
});
// Map tool calls from SDK format to API format
const toolCalls = result.toolCalls?.map((tc) => ({
name: tc.toolName,
arguments: tc.args,
}));
return {
content: result.content || "",
provider: result.provider || request.provider || "unknown",
model: result.model || request.model || "unknown",
usage: result.usage,
toolCalls,
};
}); // end withSpan
},
description: "Execute agent with prompt",
tags: ["agent"],
},
{
method: "POST",
path: `${basePath}/agent/stream`,
streaming: { enabled: true, contentType: "text/event-stream" },
handler: async (ctx) => {
// Validate request body
const validation = validateRequest(AgentExecuteRequestSchema, ctx.body, ctx.requestId);
if (!validation.success) {
return validation.error;
}
const request = validation.data;
// Normalize input
const input = typeof request.input === "string"
? { text: request.input }
: request.input;
const result = await ctx.neurolink.stream({
input,
provider: request.provider,
model: request.model,
systemPrompt: request.systemPrompt,
temperature: request.temperature,
maxTokens: request.maxTokens,
context: {
// When an authenticated user context exists (set by auth middleware),
// always use its IDs to prevent caller-supplied impersonation.
sessionId: ctx.user
? ctx.session?.id
: (ctx.session?.id ?? request.sessionId),
userId: ctx.user ? ctx.user.id : request.userId,
userEmail: ctx.user?.email,
userRoles: ctx.user?.roles,
requestId: ctx.requestId,
},
});
// Create redactor (no-op if redaction is not enabled)
const redactor = createStreamRedactor(ctx.redaction);
// Wrap stream with a span that stays open for the full consumption
// lifetime, not just the generator creation.
async function* redactedStream() {
const streamSpan = tracers.http.startSpan("neurolink.http.stream", {
attributes: {
"http.route": "/api/agent/stream",
"ai.provider": request.provider || "default",
"ai.model": request.model || "default",
},
});
try {
for await (const chunk of result.stream) {
yield redactor(chunk);
}
streamSpan.setStatus({ code: SpanStatusCode.OK });
}
catch (err) {
streamSpan.recordException(err instanceof Error ? err : new Error(String(err)));
streamSpan.setStatus({
code: SpanStatusCode.ERROR,
message: err instanceof Error ? err.message : String(err),
});
throw err;
}
finally {
streamSpan.end();
}
}
return redactedStream();
},
description: "Stream agent response",
tags: ["agent", "streaming"],
},
{
method: "GET",
path: `${basePath}/agent/providers`,
handler: async () => {
// Get available providers dynamically from ProviderFactory
const providers = ProviderFactory.getAvailableProviders();
return {
providers,
total: providers.length,
};
},
description: "List available AI providers",
tags: ["agent", "providers"],
},
{
method: "POST",
path: `${basePath}/agent/embed`,
handler: async (ctx) => {
const validation = validateRequest(EmbedRequestSchema, ctx.body, ctx.requestId);
if (!validation.success) {
return validation.error;
}
const request = validation.data;
try {
const providerName = request.provider || "openai";
return await withSpan({
name: "neurolink.http.embed",
tracer: tracers.http,
attributes: {
"http.route": "/api/agent/embed",
"ai.provider": providerName,
"ai.model": request.model || "default",
},
}, async () => {
const provider = await ProviderFactory.createProvider(providerName, request.model);
const embedding = await provider.embed(request.text, request.model);
return {
embedding,
provider: providerName,
model: request.model || "default",
dimension: embedding.length,
};
});
}
catch (error) {
return createError("EXECUTION_FAILED", error instanceof Error
? error.message
: "Embedding generation failed", undefined, ctx.requestId);
}
},
description: "Generate embedding for a single text",
tags: ["agent", "embeddings"],
},
{
method: "POST",
path: `${basePath}/agent/embed-many`,
handler: async (ctx) => {
const validation = validateRequest(EmbedManyRequestSchema, ctx.body, ctx.requestId);
if (!validation.success) {
return validation.error;
}
const request = validation.data;
try {
const providerName = request.provider || "openai";
return await withSpan({
name: "neurolink.http.embedMany",
tracer: tracers.http,
attributes: {
"http.route": "/api/agent/embed-many",
"ai.provider": providerName,
"ai.model": request.model || "default",
"ai.embed.count": request.texts.length,
},
}, async () => {
const provider = await ProviderFactory.createProvider(providerName, request.model);
const embeddings = await provider.embedMany(request.texts, request.model);
return {
embeddings,
provider: providerName,
model: request.model || "default",
count: embeddings.length,
dimension: embeddings[0]?.length ?? 0,
};
});
}
catch (error) {
return createError("EXECUTION_FAILED", error instanceof Error
? error.message
: "Batch embedding generation failed", undefined, ctx.requestId);
}
},
description: "Generate embeddings for multiple texts in a batch",
tags: ["agent", "embeddings"],
},
],
};
}
//# sourceMappingURL=agentRoutes.js.map