express-genix 4.5.2 → 4.6.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.
@@ -1,84 +0,0 @@
1
- const { createReactAgent } = require('@langchain/langgraph/prebuilt');
2
- const { tool } = require('@langchain/core/tools');
3
- const { HumanMessage } = require('@langchain/core/messages');
4
- const { z } = require('zod');
5
- const { getModel } = require('../config/ai');
6
-
7
- // --- Built-in Tools ---
8
-
9
- const currentTimeTool = tool(
10
- async () => new Date().toISOString(),
11
- {
12
- name: 'current_time',
13
- description: 'Get the current date and time in ISO 8601 format',
14
- schema: z.object({}),
15
- }
16
- );
17
-
18
- const calculatorTool = tool(
19
- async ({ expression }) => {
20
- // Only allow digits and math operators — no code injection possible
21
- const sanitized = expression.replace(/[^0-9+\-*/().%\s]/g, '');
22
- if (sanitized !== expression.trim()) {
23
- return 'Error: Expression contains invalid characters. Use only numbers and +, -, *, /, (, ), %';
24
- }
25
- try {
26
- const result = new Function(`"use strict"; return (${sanitized})`)();
27
- if (!isFinite(result)) return 'Error: Result is not a finite number';
28
- return String(result);
29
- } catch {
30
- return 'Error: Could not evaluate expression';
31
- }
32
- },
33
- {
34
- name: 'calculator',
35
- description: 'Evaluate a mathematical expression using standard operators: +, -, *, /, %',
36
- schema: z.object({
37
- expression: z.string().describe('Math expression, e.g. "2 + 3 * 4"'),
38
- }),
39
- }
40
- );
41
-
42
- /**
43
- * Create a LangGraph ReAct agent with tool-calling capabilities.
44
- *
45
- * @param {Object} options
46
- * @param {Array} options.tools - Additional @langchain/core tools to register
47
- * @param {string} options.systemPrompt - System instruction for the agent
48
- * @param {string} options.model - Model name override
49
- */
50
- const createAgent = (options = {}) => {
51
- const tools = [
52
- currentTimeTool,
53
- calculatorTool,
54
- ...(options.tools || []),
55
- ];
56
-
57
- const model = getModel(options);
58
-
59
- return createReactAgent({
60
- llm: model,
61
- tools,
62
- messageModifier: options.systemPrompt || 'You are a helpful assistant. Use the provided tools when needed to answer questions accurately.',
63
- });
64
- };
65
-
66
- /**
67
- * Run the agent with a single message and return the final response.
68
- */
69
- const runAgent = async (message, options = {}) => {
70
- const agent = createAgent(options);
71
-
72
- const result = await agent.invoke({
73
- messages: [new HumanMessage(message)],
74
- });
75
-
76
- const lastMessage = result.messages[result.messages.length - 1];
77
-
78
- return {
79
- content: lastMessage.content,
80
- steps: result.messages.length,
81
- };
82
- };
83
-
84
- module.exports = { createAgent, runAgent, currentTimeTool, calculatorTool };
@@ -1,47 +0,0 @@
1
- <% if (aiProvider === 'openai') { %>
2
- const { ChatOpenAI } = require('@langchain/openai');
3
- <% } else if (aiProvider === 'anthropic') { %>
4
- const { ChatAnthropic } = require('@langchain/anthropic');
5
- <% } else if (aiProvider === 'gemini') { %>
6
- const { ChatGoogleGenerativeAI } = require('@langchain/google-genai');
7
- <% } else if (aiProvider === 'ollama') { %>
8
- const { ChatOllama } = require('@langchain/ollama');
9
- <% } %>
10
-
11
- /**
12
- * Create a LangChain chat model based on the configured provider.
13
- */
14
- const getModel = (options = {}) => {
15
- const temperature = options.temperature ?? parseFloat(process.env.AI_TEMPERATURE || '0.7');
16
- const maxTokens = options.maxTokens ?? parseInt(process.env.AI_MAX_TOKENS || '2048', 10);
17
- <% if (aiProvider === 'openai') { %>
18
- return new ChatOpenAI({
19
- modelName: options.model || process.env.AI_MODEL || 'gpt-4o-mini',
20
- temperature,
21
- maxTokens,
22
- openAIApiKey: process.env.OPENAI_API_KEY,
23
- });
24
- <% } else if (aiProvider === 'anthropic') { %>
25
- return new ChatAnthropic({
26
- modelName: options.model || process.env.AI_MODEL || 'claude-sonnet-4-20250514',
27
- temperature,
28
- maxTokens,
29
- anthropicApiKey: process.env.ANTHROPIC_API_KEY,
30
- });
31
- <% } else if (aiProvider === 'gemini') { %>
32
- return new ChatGoogleGenerativeAI({
33
- model: options.model || process.env.AI_MODEL || 'gemini-2.0-flash',
34
- temperature,
35
- maxOutputTokens: maxTokens,
36
- apiKey: process.env.GOOGLE_API_KEY,
37
- });
38
- <% } else if (aiProvider === 'ollama') { %>
39
- return new ChatOllama({
40
- model: options.model || process.env.AI_MODEL || 'llama3',
41
- temperature,
42
- baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434',
43
- });
44
- <% } %>
45
- };
46
-
47
- module.exports = { getModel };
@@ -1,100 +0,0 @@
1
- const aiService = require('../services/aiService');
2
- const { runAgent } = require('../agents/graph');
3
- const { success, error } = require('../utils/response');
4
-
5
- /**
6
- * POST /ai/chat — Send a message, get a complete AI response.
7
- */
8
- const chatHandler = async (req, res, next) => {
9
- try {
10
- const { message, systemPrompt, history, model, temperature, maxTokens } = req.body;
11
-
12
- if (!message) {
13
- return error(res, 'Message is required', 400);
14
- }
15
-
16
- const result = await aiService.chat(message, {
17
- systemPrompt,
18
- history,
19
- model,
20
- temperature,
21
- maxTokens,
22
- });
23
-
24
- return success(res, result);
25
- } catch (err) {
26
- next(err);
27
- }
28
- };
29
-
30
- /**
31
- * POST /ai/stream — Stream an AI response via Server-Sent Events.
32
- */
33
- const streamHandler = async (req, res, next) => {
34
- try {
35
- const { message, systemPrompt, history, model, temperature, maxTokens } = req.body;
36
-
37
- if (!message) {
38
- return error(res, 'Message is required', 400);
39
- }
40
-
41
- res.setHeader('Content-Type', 'text/event-stream');
42
- res.setHeader('Cache-Control', 'no-cache');
43
- res.setHeader('Connection', 'keep-alive');
44
-
45
- const generator = aiService.stream(message, {
46
- systemPrompt,
47
- history,
48
- model,
49
- temperature,
50
- maxTokens,
51
- });
52
-
53
- for await (const chunk of generator) {
54
- res.write(`data: ${JSON.stringify({ content: chunk })}\n\n`);
55
- }
56
-
57
- res.write('data: [DONE]\n\n');
58
- res.end();
59
- } catch (err) {
60
- next(err);
61
- }
62
- };
63
-
64
- /**
65
- * POST /ai/chain — Run a prompt template chain with variables.
66
- */
67
- const chainHandler = async (req, res, next) => {
68
- try {
69
- const { template, variables, model } = req.body;
70
-
71
- if (!template) {
72
- return error(res, 'Template is required', 400);
73
- }
74
-
75
- const result = await aiService.chain(template, variables || {}, { model });
76
- return success(res, { content: result });
77
- } catch (err) {
78
- next(err);
79
- }
80
- };
81
-
82
- /**
83
- * POST /ai/agent — Run the LangGraph ReAct agent.
84
- */
85
- const agentHandler = async (req, res, next) => {
86
- try {
87
- const { message, systemPrompt, model } = req.body;
88
-
89
- if (!message) {
90
- return error(res, 'Message is required', 400);
91
- }
92
-
93
- const result = await runAgent(message, { systemPrompt, model });
94
- return success(res, result);
95
- } catch (err) {
96
- next(err);
97
- }
98
- };
99
-
100
- module.exports = { chatHandler, streamHandler, chainHandler, agentHandler };
@@ -1,117 +0,0 @@
1
- const express = require('express');
2
- const { chatHandler, streamHandler, chainHandler, agentHandler } = require('../controllers/aiController');
3
-
4
- const router = express.Router();
5
-
6
- /**
7
- * @swagger
8
- * <% if (hasApiVersioning) { %>/v1<% } %>/ai/chat:
9
- * post:
10
- * summary: Send a chat message to the AI
11
- * tags: [AI]
12
- * requestBody:
13
- * required: true
14
- * content:
15
- * application/json:
16
- * schema:
17
- * type: object
18
- * required: [message]
19
- * properties:
20
- * message:
21
- * type: string
22
- * example: "What is Node.js?"
23
- * systemPrompt:
24
- * type: string
25
- * example: "You are a helpful coding assistant."
26
- * history:
27
- * type: array
28
- * items:
29
- * type: object
30
- * properties:
31
- * role:
32
- * type: string
33
- * enum: [user, assistant]
34
- * content:
35
- * type: string
36
- * model:
37
- * type: string
38
- * responses:
39
- * 200:
40
- * description: AI response
41
- */
42
- router.post('/chat', chatHandler);
43
-
44
- /**
45
- * @swagger
46
- * <% if (hasApiVersioning) { %>/v1<% } %>/ai/stream:
47
- * post:
48
- * summary: Stream an AI response via Server-Sent Events
49
- * tags: [AI]
50
- * requestBody:
51
- * required: true
52
- * content:
53
- * application/json:
54
- * schema:
55
- * type: object
56
- * required: [message]
57
- * properties:
58
- * message:
59
- * type: string
60
- * responses:
61
- * 200:
62
- * description: SSE stream of AI response chunks
63
- */
64
- router.post('/stream', streamHandler);
65
-
66
- /**
67
- * @swagger
68
- * <% if (hasApiVersioning) { %>/v1<% } %>/ai/chain:
69
- * post:
70
- * summary: Run a prompt template chain with variable substitution
71
- * tags: [AI]
72
- * requestBody:
73
- * required: true
74
- * content:
75
- * application/json:
76
- * schema:
77
- * type: object
78
- * required: [template]
79
- * properties:
80
- * template:
81
- * type: string
82
- * example: "Summarize the following text: {text}"
83
- * variables:
84
- * type: object
85
- * example: { "text": "Node.js is a JavaScript runtime..." }
86
- * responses:
87
- * 200:
88
- * description: Chain result
89
- */
90
- router.post('/chain', chainHandler);
91
-
92
- /**
93
- * @swagger
94
- * <% if (hasApiVersioning) { %>/v1<% } %>/ai/agent:
95
- * post:
96
- * summary: Run the LangGraph ReAct agent with tool calling
97
- * tags: [AI]
98
- * requestBody:
99
- * required: true
100
- * content:
101
- * application/json:
102
- * schema:
103
- * type: object
104
- * required: [message]
105
- * properties:
106
- * message:
107
- * type: string
108
- * example: "What time is it right now?"
109
- * systemPrompt:
110
- * type: string
111
- * responses:
112
- * 200:
113
- * description: Agent response with tool-use steps
114
- */
115
- router.post('/agent', agentHandler);
116
-
117
- module.exports = router;
@@ -1,80 +0,0 @@
1
- const { getModel } = require('../config/ai');
2
- const { HumanMessage, SystemMessage, AIMessage } = require('@langchain/core/messages');
3
- const { StringOutputParser } = require('@langchain/core/output_parsers');
4
- const { ChatPromptTemplate } = require('@langchain/core/prompts');
5
- const { logger } = require('../utils/logger');
6
-
7
- const DEFAULT_SYSTEM_PROMPT = 'You are a helpful AI assistant.';
8
-
9
- /**
10
- * Send a chat message and get a complete response.
11
- */
12
- const chat = async (message, options = {}) => {
13
- const model = getModel(options);
14
- const messages = [
15
- new SystemMessage(options.systemPrompt || DEFAULT_SYSTEM_PROMPT),
16
- ];
17
-
18
- if (Array.isArray(options.history)) {
19
- for (const msg of options.history) {
20
- if (msg.role === 'user') messages.push(new HumanMessage(msg.content));
21
- else if (msg.role === 'assistant') messages.push(new AIMessage(msg.content));
22
- }
23
- }
24
-
25
- messages.push(new HumanMessage(message));
26
-
27
- const response = await model.invoke(messages);
28
-
29
- logger.info('AI chat completed', {
30
- provider: process.env.AI_PROVIDER,
31
- });
32
-
33
- return {
34
- content: response.content,
35
- model: response.response_metadata?.model || 'unknown',
36
- usage: response.usage_metadata || null,
37
- };
38
- };
39
-
40
- /**
41
- * Stream a chat response. Returns an async generator yielding text chunks.
42
- */
43
- const stream = async function* (message, options = {}) {
44
- const model = getModel(options);
45
- const messages = [
46
- new SystemMessage(options.systemPrompt || DEFAULT_SYSTEM_PROMPT),
47
- ];
48
-
49
- if (Array.isArray(options.history)) {
50
- for (const msg of options.history) {
51
- if (msg.role === 'user') messages.push(new HumanMessage(msg.content));
52
- else if (msg.role === 'assistant') messages.push(new AIMessage(msg.content));
53
- }
54
- }
55
-
56
- messages.push(new HumanMessage(message));
57
-
58
- const streamResponse = await model.stream(messages);
59
-
60
- for await (const chunk of streamResponse) {
61
- if (chunk.content) {
62
- yield chunk.content;
63
- }
64
- }
65
- };
66
-
67
- /**
68
- * Run a prompt template chain with variable substitution.
69
- */
70
- const chain = async (template, variables = {}, options = {}) => {
71
- const model = getModel(options);
72
- const prompt = ChatPromptTemplate.fromTemplate(template);
73
- const outputParser = new StringOutputParser();
74
- const pipeline = prompt.pipe(model).pipe(outputParser);
75
-
76
- const result = await pipeline.invoke(variables);
77
- return result;
78
- };
79
-
80
- module.exports = { chat, stream, chain };