agentnet 0.0.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.
- package/LICENSE.txt +202 -0
- package/README.md +488 -0
- package/package.json +23 -0
- package/src/agent/agent-loader.js +274 -0
- package/src/agent/agent.js +416 -0
- package/src/agent/client.js +14 -0
- package/src/agent/executor.js +320 -0
- package/src/agent/runtime.js +142 -0
- package/src/agent/runtimes/nats.js +379 -0
- package/src/errors/index.js +195 -0
- package/src/examples/agents-smartness.yaml +308 -0
- package/src/examples/agents.yaml +394 -0
- package/src/examples/def.js +74 -0
- package/src/examples/def2.js +65 -0
- package/src/examples/def3.js +67 -0
- package/src/examples/simple.js +103 -0
- package/src/index.js +115 -0
- package/src/llm/gemini.js +155 -0
- package/src/llm/gpt.js +155 -0
- package/src/store/store.js +167 -0
- package/src/utils/logger.js +209 -0
- package/src/utils/store.js +212 -0
- package/src/utils/validation.js +287 -0
package/src/llm/gpt.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import OpenAI from 'openai'
|
|
2
|
+
import { logger } from '../utils/logger.js'
|
|
3
|
+
import { LLMError } from '../errors/index.js'
|
|
4
|
+
|
|
5
|
+
const type = 'openai'
|
|
6
|
+
|
|
7
|
+
const getClient = async function () {
|
|
8
|
+
try {
|
|
9
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
10
|
+
throw new LLMError(
|
|
11
|
+
'OPENAI_API_KEY environment variable is not set',
|
|
12
|
+
'openai'
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
logger.debug('Initializing OpenAI client');
|
|
17
|
+
return new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger.error('Failed to initialize OpenAI client', { error });
|
|
20
|
+
throw new LLMError(
|
|
21
|
+
`Failed to initialize OpenAI client: ${error.message}`,
|
|
22
|
+
'openai',
|
|
23
|
+
{ originalError: error }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const callModel = async function (llmClientConfig, context) {
|
|
29
|
+
const client = context.client
|
|
30
|
+
const toolsAndHandoffsMap = context.toolsAndHandoffsMap
|
|
31
|
+
const conversation = context.conversation
|
|
32
|
+
const input = {}
|
|
33
|
+
Object.assign(input, llmClientConfig)
|
|
34
|
+
input['tools'] = toolsAndHandoffsMap.tools
|
|
35
|
+
input['input'] = conversation
|
|
36
|
+
|
|
37
|
+
logger.debug('Calling OpenAI model', {
|
|
38
|
+
model: input.model,
|
|
39
|
+
conversationLength: conversation.length,
|
|
40
|
+
toolsCount: toolsAndHandoffsMap.tools.length
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const response = await client.responses.create(input)
|
|
45
|
+
logger.debug('OpenAI response received');
|
|
46
|
+
return response
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.error('OpenAI API error', {
|
|
49
|
+
error,
|
|
50
|
+
modelName: input.model
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
throw new LLMError(
|
|
54
|
+
`OpenAI API error: ${error.message}`,
|
|
55
|
+
'openai',
|
|
56
|
+
{
|
|
57
|
+
statusCode: error.status || error.statusCode,
|
|
58
|
+
modelName: input.model
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const onResponse = async function (state, conversation, toolsAndHandoffsMap, response) {
|
|
65
|
+
if (response.output_text !== undefined && response.output_text.length > 0) {
|
|
66
|
+
logger.debug('OpenAI response contains text, returning directly');
|
|
67
|
+
conversation.push({ role: 'model', parts: [{ text: response.output_text }] });
|
|
68
|
+
return response.output_text
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const reasoning = response.output.filter(x => x.type == 'reasoning')
|
|
72
|
+
const functionCalls = response.output.filter(x => x.type == 'function_call')
|
|
73
|
+
|
|
74
|
+
logger.debug('OpenAI response processing', {
|
|
75
|
+
reasoningCount: reasoning.length,
|
|
76
|
+
functionCallCount: functionCalls.length
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
for (const res of reasoning) {
|
|
80
|
+
conversation.push(res)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const toolCall of functionCalls) {
|
|
84
|
+
try {
|
|
85
|
+
const args = JSON.parse(toolCall.arguments)
|
|
86
|
+
const name = toolCall.name
|
|
87
|
+
|
|
88
|
+
logger.debug('Executing tool from OpenAI', {
|
|
89
|
+
toolName: name,
|
|
90
|
+
argsPreview: JSON.stringify(args).substring(0, 100),
|
|
91
|
+
callId: toolCall.call_id
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!toolsAndHandoffsMap[name] || !toolsAndHandoffsMap[name].function) {
|
|
95
|
+
throw new Error(`Tool "${name}" not found or has no function implementation`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let result = await toolsAndHandoffsMap[name].function(conversation, state, args)
|
|
99
|
+
conversation.push(toolCall)
|
|
100
|
+
if (toolsAndHandoffsMap[name].type === 'handoff') {
|
|
101
|
+
console.log("GPT HANDOFF onResponse", name, result)
|
|
102
|
+
const resultParsed = JSON.parse(result)
|
|
103
|
+
// Update state with the result
|
|
104
|
+
if (resultParsed.session) {
|
|
105
|
+
for (const key of Object.keys(resultParsed.session)) {
|
|
106
|
+
state[key] = resultParsed.session[key]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const resultString = typeof result == 'string' ? result : JSON.stringify(result)
|
|
112
|
+
|
|
113
|
+
logger.debug('Tool execution successful', {
|
|
114
|
+
toolName: name,
|
|
115
|
+
resultPreview: resultString.substring(0, 100)
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
conversation.push({
|
|
119
|
+
type: "function_call_output",
|
|
120
|
+
call_id: toolCall.call_id,
|
|
121
|
+
output: resultString
|
|
122
|
+
})
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error(`Error executing tool "${toolCall.name}"`, { error });
|
|
125
|
+
|
|
126
|
+
// Add error as function output
|
|
127
|
+
conversation.push(toolCall);
|
|
128
|
+
conversation.push({
|
|
129
|
+
type: "function_call_output",
|
|
130
|
+
call_id: toolCall.call_id,
|
|
131
|
+
output: JSON.stringify({ error: error.message })
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const prompt = async function (conversation, formattedPrompt) {
|
|
139
|
+
logger.debug('Adding user prompt to conversation', {
|
|
140
|
+
promptPreview: formattedPrompt.substring(0, 100)
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
conversation.push({
|
|
144
|
+
role: 'user',
|
|
145
|
+
content: formattedPrompt
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default {
|
|
150
|
+
type,
|
|
151
|
+
getClient,
|
|
152
|
+
prompt,
|
|
153
|
+
callModel,
|
|
154
|
+
onResponse
|
|
155
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid'
|
|
2
|
+
import { createClient } from 'redis'
|
|
3
|
+
import pg from 'pg'
|
|
4
|
+
|
|
5
|
+
let config = {
|
|
6
|
+
user: process.env.PG_USER || 'postgres',
|
|
7
|
+
host: process.env.PG_HOST || 'localhost',
|
|
8
|
+
database: process.env.PG_DATABASE || 'postgres',
|
|
9
|
+
password: process.env.PG_PASSWORD || 'password',
|
|
10
|
+
port: process.env.PG_PORT || 5433
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (process.env.PG_USE_SSL == 'true' || process.env.PG_USE_SSL == true) {
|
|
14
|
+
config.ssl = {
|
|
15
|
+
rejectUnauthorized: false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pool = new pg.Pool(config)
|
|
20
|
+
|
|
21
|
+
pool.on('error', err => {
|
|
22
|
+
console.log(new Date(), 'Lost Postgres connection', err)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export async function getClient() {
|
|
26
|
+
const client = await pool.connect()
|
|
27
|
+
return client
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function session (id) {
|
|
31
|
+
let state = {}
|
|
32
|
+
let conversation = []
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
query: async function (agentInstance, input) {
|
|
36
|
+
return await agentInstance.query(state, conversation, input)
|
|
37
|
+
},
|
|
38
|
+
setState: function (_state) {
|
|
39
|
+
state = _state
|
|
40
|
+
},
|
|
41
|
+
mergeState: function (_state) {
|
|
42
|
+
Object.keys(_state).forEach((key) => {
|
|
43
|
+
state[key] = _state[key]
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
getState: function () {
|
|
47
|
+
return state
|
|
48
|
+
},
|
|
49
|
+
trimConversation: function (elementsToKeep) {
|
|
50
|
+
conversation = conversation.slice(-elementsToKeep)
|
|
51
|
+
let additionalElementsToRemove = 0
|
|
52
|
+
for (const chatIndex in conversation) {
|
|
53
|
+
if (conversation[chatIndex].role !== 'user' || conversation[chatIndex].role == undefined) {
|
|
54
|
+
additionalElementsToRemove += 1
|
|
55
|
+
} else {
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (additionalElementsToRemove > 0) {
|
|
60
|
+
conversation = conversation.slice(additionalElementsToRemove)
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
setConversation: function (_conversation) {
|
|
64
|
+
conversation = _conversation
|
|
65
|
+
},
|
|
66
|
+
getConversation: function () {
|
|
67
|
+
return conversation
|
|
68
|
+
},
|
|
69
|
+
load: async function (stateStore) {
|
|
70
|
+
const _state = await stateStore.get(id)
|
|
71
|
+
if (_state !== null) {
|
|
72
|
+
const parsedState = JSON.parse(_state)
|
|
73
|
+
conversation = parsedState.conversation || []
|
|
74
|
+
state = parsedState.state || {}
|
|
75
|
+
return {
|
|
76
|
+
conversation: conversation,
|
|
77
|
+
state: state
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
conversation: [],
|
|
82
|
+
state: {}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
dump: async function (stateStore) {
|
|
86
|
+
return await stateStore.set(id, JSON.stringify({
|
|
87
|
+
conversation: conversation,
|
|
88
|
+
state: state
|
|
89
|
+
}))
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function redisStore (_config = null) {
|
|
95
|
+
let client = null
|
|
96
|
+
let config = _config
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
connect: async function () {
|
|
100
|
+
if (!client) {
|
|
101
|
+
client = createClient(config || { url: 'redis://localhost:6379' })
|
|
102
|
+
client.connect()
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
disconnect: async function () {
|
|
106
|
+
if (client) {
|
|
107
|
+
await client.disconnect()
|
|
108
|
+
client = null
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
set: async function (key, value) {
|
|
112
|
+
return await client.set(key, value)
|
|
113
|
+
},
|
|
114
|
+
get: async function (key) {
|
|
115
|
+
return await client.get(key)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function postgresStore (_config = null) {
|
|
121
|
+
let client = null
|
|
122
|
+
let config = _config
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
connect: async function () {
|
|
126
|
+
if (!client) {
|
|
127
|
+
client = await getClient()
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
disconnect: async function () {
|
|
131
|
+
if (client) {
|
|
132
|
+
await client.end()
|
|
133
|
+
client = null
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
set: async function (key, value) {
|
|
137
|
+
const id = uuid()
|
|
138
|
+
return await client.query('INSERT INTO smartchat_agent.conversation_state (state_id, state, id) VALUES ($1,$2,$3) ON CONFLICT (state_id) DO UPDATE SET state_id=$1, state=$2', [key, value, id])
|
|
139
|
+
},
|
|
140
|
+
get: async function (key) {
|
|
141
|
+
const res = await client.query('SELECT state FROM smartchat_agent.conversation_state WHERE state_id=$1', [key])
|
|
142
|
+
if (res.rows.length == 1) {
|
|
143
|
+
return res.rows[0].state
|
|
144
|
+
}
|
|
145
|
+
if (res.rows.length > 1) {
|
|
146
|
+
throw 'Something went wrong at pg store'
|
|
147
|
+
}
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function memoryStore () {
|
|
154
|
+
let state = {}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
connect: async function () {},
|
|
158
|
+
disconnect: async function () {},
|
|
159
|
+
set: async function (key, value) {
|
|
160
|
+
state[key] = value
|
|
161
|
+
return state[key]
|
|
162
|
+
},
|
|
163
|
+
get: async function (key) {
|
|
164
|
+
return state[key] || null
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging levels
|
|
3
|
+
*/
|
|
4
|
+
export const LogLevel = {
|
|
5
|
+
ERROR: 'error',
|
|
6
|
+
WARN: 'warn',
|
|
7
|
+
INFO: 'info',
|
|
8
|
+
DEBUG: 'debug',
|
|
9
|
+
TRACE: 'trace'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default logger configuration
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_CONFIG = {
|
|
16
|
+
level: LogLevel.INFO,
|
|
17
|
+
enableTimestamps: true,
|
|
18
|
+
enableColors: true,
|
|
19
|
+
redactSensitiveData: true,
|
|
20
|
+
sensitiveKeys: ['api_key', 'key', 'token', 'password', 'secret', 'credential'],
|
|
21
|
+
maxOutputLength: 1000
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ANSI color codes for console output
|
|
25
|
+
const COLORS = {
|
|
26
|
+
reset: '\x1b[0m',
|
|
27
|
+
red: '\x1b[31m',
|
|
28
|
+
green: '\x1b[32m',
|
|
29
|
+
yellow: '\x1b[33m',
|
|
30
|
+
blue: '\x1b[34m',
|
|
31
|
+
magenta: '\x1b[35m',
|
|
32
|
+
cyan: '\x1b[36m',
|
|
33
|
+
gray: '\x1b[90m'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Formats log message with timestamp and level
|
|
38
|
+
*/
|
|
39
|
+
function formatLogMessage(level, message, config) {
|
|
40
|
+
let output = '';
|
|
41
|
+
|
|
42
|
+
// Add timestamp
|
|
43
|
+
if (config.enableTimestamps) {
|
|
44
|
+
const timestamp = new Date().toISOString();
|
|
45
|
+
output += config.enableColors ? `${COLORS.gray}${timestamp}${COLORS.reset} ` : `${timestamp} `;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add log level
|
|
49
|
+
if (config.enableColors) {
|
|
50
|
+
const levelColor = {
|
|
51
|
+
[LogLevel.ERROR]: COLORS.red,
|
|
52
|
+
[LogLevel.WARN]: COLORS.yellow,
|
|
53
|
+
[LogLevel.INFO]: COLORS.green,
|
|
54
|
+
[LogLevel.DEBUG]: COLORS.blue,
|
|
55
|
+
[LogLevel.TRACE]: COLORS.gray
|
|
56
|
+
}[level] || COLORS.reset;
|
|
57
|
+
|
|
58
|
+
output += `${levelColor}[${level.toUpperCase()}]${COLORS.reset} `;
|
|
59
|
+
} else {
|
|
60
|
+
output += `[${level.toUpperCase()}] `;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Add message
|
|
64
|
+
output += message;
|
|
65
|
+
|
|
66
|
+
return output;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Redacts sensitive data in objects
|
|
71
|
+
*/
|
|
72
|
+
function redactSensitiveData(data, sensitiveKeys) {
|
|
73
|
+
if (!data || typeof data !== 'object') {
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (Array.isArray(data)) {
|
|
78
|
+
return data.map(item => redactSensitiveData(item, sensitiveKeys));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Clone the object to avoid modifying the original
|
|
82
|
+
const result = { ...data };
|
|
83
|
+
|
|
84
|
+
for (const key in result) {
|
|
85
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
86
|
+
// Check if key name contains any sensitive patterns
|
|
87
|
+
const isSensitive = sensitiveKeys.some(pattern =>
|
|
88
|
+
key.toLowerCase().includes(pattern.toLowerCase())
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (isSensitive) {
|
|
92
|
+
// Redact the value
|
|
93
|
+
result[key] = '[REDACTED]';
|
|
94
|
+
} else if (typeof result[key] === 'object' && result[key] !== null) {
|
|
95
|
+
// Recursively process nested objects
|
|
96
|
+
result[key] = redactSensitiveData(result[key], sensitiveKeys);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Safely stringifies objects for logging
|
|
106
|
+
*/
|
|
107
|
+
function safeStringify(obj, maxLength) {
|
|
108
|
+
try {
|
|
109
|
+
if (obj instanceof Error) {
|
|
110
|
+
return obj.stack || obj.message;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
114
|
+
let str = JSON.stringify(obj, null, 2);
|
|
115
|
+
if (maxLength && str.length > maxLength) {
|
|
116
|
+
str = str.substring(0, maxLength) + '... [truncated]';
|
|
117
|
+
}
|
|
118
|
+
return str;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return String(obj);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return `[Unstringifiable Object: ${err.message}]`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates a logger instance
|
|
129
|
+
*/
|
|
130
|
+
export function createLogger(customConfig = {}) {
|
|
131
|
+
const config = { ...DEFAULT_CONFIG, ...customConfig };
|
|
132
|
+
|
|
133
|
+
// Log level priority map
|
|
134
|
+
const levelPriority = {
|
|
135
|
+
[LogLevel.ERROR]: 0,
|
|
136
|
+
[LogLevel.WARN]: 1,
|
|
137
|
+
[LogLevel.INFO]: 2,
|
|
138
|
+
[LogLevel.DEBUG]: 3,
|
|
139
|
+
[LogLevel.TRACE]: 4
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Internal logging function
|
|
144
|
+
*/
|
|
145
|
+
function log(level, message, data = {}) {
|
|
146
|
+
// Check if this log level should be shown
|
|
147
|
+
if (levelPriority[level] > levelPriority[config.level]) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Handle error objects
|
|
152
|
+
if (message instanceof Error) {
|
|
153
|
+
data.stack = message.stack;
|
|
154
|
+
message = message.message;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Redact sensitive data if configured
|
|
158
|
+
let processedData = data;
|
|
159
|
+
if (config.redactSensitiveData && typeof data === 'object') {
|
|
160
|
+
processedData = redactSensitiveData(data, config.sensitiveKeys);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Format the basic message
|
|
164
|
+
let logMessage = formatLogMessage(level, message, config);
|
|
165
|
+
|
|
166
|
+
// Add contextual data if available
|
|
167
|
+
if (processedData && Object.keys(processedData).length > 0) {
|
|
168
|
+
logMessage += '\n' + safeStringify(processedData, config.maxOutputLength);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Output to console
|
|
172
|
+
switch (level) {
|
|
173
|
+
case LogLevel.ERROR:
|
|
174
|
+
console.error(logMessage);
|
|
175
|
+
break;
|
|
176
|
+
case LogLevel.WARN:
|
|
177
|
+
console.warn(logMessage);
|
|
178
|
+
break;
|
|
179
|
+
case LogLevel.INFO:
|
|
180
|
+
console.info(logMessage);
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
console.log(logMessage);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
error: (message, data) => log(LogLevel.ERROR, message, data),
|
|
189
|
+
warn: (message, data) => log(LogLevel.WARN, message, data),
|
|
190
|
+
info: (message, data) => log(LogLevel.INFO, message, data),
|
|
191
|
+
debug: (message, data) => log(LogLevel.DEBUG, message, data),
|
|
192
|
+
trace: (message, data) => log(LogLevel.TRACE, message, data),
|
|
193
|
+
|
|
194
|
+
// Allow changing configuration at runtime
|
|
195
|
+
setLevel: (level) => {
|
|
196
|
+
if (Object.values(LogLevel).includes(level)) {
|
|
197
|
+
config.level = level;
|
|
198
|
+
} else {
|
|
199
|
+
log(LogLevel.WARN, `Invalid log level: ${level}. Using ${config.level}`);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Get the current logger configuration
|
|
204
|
+
getConfig: () => ({ ...config })
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Create and export default logger
|
|
209
|
+
export const logger = createLogger();
|