@nextsparkjs/plugin-langchain 0.1.0-beta.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.
Files changed (67) hide show
  1. package/.env.example +41 -0
  2. package/api/observability/metrics/route.ts +110 -0
  3. package/api/observability/traces/[traceId]/route.ts +398 -0
  4. package/api/observability/traces/route.ts +205 -0
  5. package/api/sessions/route.ts +332 -0
  6. package/components/observability/CollapsibleJson.tsx +71 -0
  7. package/components/observability/CompactTimeline.tsx +75 -0
  8. package/components/observability/ConversationFlow.tsx +271 -0
  9. package/components/observability/DisabledMessage.tsx +21 -0
  10. package/components/observability/FiltersPanel.tsx +82 -0
  11. package/components/observability/ObservabilityDashboard.tsx +230 -0
  12. package/components/observability/SpansList.tsx +210 -0
  13. package/components/observability/TraceDetail.tsx +335 -0
  14. package/components/observability/TraceStatusBadge.tsx +39 -0
  15. package/components/observability/TracesTable.tsx +97 -0
  16. package/components/observability/index.ts +7 -0
  17. package/docs/01-getting-started/01-overview.md +196 -0
  18. package/docs/01-getting-started/02-installation.md +368 -0
  19. package/docs/01-getting-started/03-configuration.md +794 -0
  20. package/docs/02-core-concepts/01-architecture.md +566 -0
  21. package/docs/02-core-concepts/02-agents.md +597 -0
  22. package/docs/02-core-concepts/03-tools.md +689 -0
  23. package/docs/03-orchestration/01-graph-orchestrator.md +809 -0
  24. package/docs/03-orchestration/02-legacy-react.md +650 -0
  25. package/docs/04-advanced/01-observability.md +645 -0
  26. package/docs/04-advanced/02-token-tracking.md +469 -0
  27. package/docs/04-advanced/03-streaming.md +476 -0
  28. package/docs/04-advanced/04-guardrails.md +597 -0
  29. package/docs/05-reference/01-api-reference.md +1403 -0
  30. package/docs/05-reference/02-customization.md +646 -0
  31. package/docs/05-reference/03-examples.md +881 -0
  32. package/docs/index.md +85 -0
  33. package/hooks/observability/useMetrics.ts +31 -0
  34. package/hooks/observability/useTraceDetail.ts +48 -0
  35. package/hooks/observability/useTraces.ts +59 -0
  36. package/lib/agent-factory.ts +354 -0
  37. package/lib/agent-helpers.ts +201 -0
  38. package/lib/db-memory-store.ts +417 -0
  39. package/lib/graph/index.ts +58 -0
  40. package/lib/graph/nodes/combiner.ts +399 -0
  41. package/lib/graph/nodes/router.ts +440 -0
  42. package/lib/graph/orchestrator-graph.ts +386 -0
  43. package/lib/graph/prompts/combiner.md +131 -0
  44. package/lib/graph/prompts/router.md +193 -0
  45. package/lib/graph/types.ts +365 -0
  46. package/lib/guardrails.ts +230 -0
  47. package/lib/index.ts +44 -0
  48. package/lib/logger.ts +70 -0
  49. package/lib/memory-store.ts +168 -0
  50. package/lib/message-serializer.ts +110 -0
  51. package/lib/prompt-renderer.ts +94 -0
  52. package/lib/providers.ts +226 -0
  53. package/lib/streaming.ts +232 -0
  54. package/lib/token-tracker.ts +298 -0
  55. package/lib/tools-builder.ts +192 -0
  56. package/lib/tracer-callbacks.ts +342 -0
  57. package/lib/tracer.ts +350 -0
  58. package/migrations/001_langchain_memory.sql +83 -0
  59. package/migrations/002_token_usage.sql +127 -0
  60. package/migrations/003_observability.sql +257 -0
  61. package/package.json +28 -0
  62. package/plugin.config.ts +170 -0
  63. package/presets/lib/langchain.config.ts.preset +142 -0
  64. package/presets/templates/sector7/ai-observability/[traceId]/page.tsx +91 -0
  65. package/presets/templates/sector7/ai-observability/page.tsx +54 -0
  66. package/types/langchain.types.ts +274 -0
  67. package/types/observability.types.ts +270 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * LangGraph Orchestrator Types
3
+ *
4
+ * State schema and types for the explicit graph-based orchestrator.
5
+ * Replaces ReAct loops with deterministic state machine flow.
6
+ *
7
+ * GENERIC ARCHITECTURE:
8
+ * - Plugin defines generic interfaces (AgentTool, OrchestratorConfig)
9
+ * - Theme provides tool implementations and configuration
10
+ * - No hardcoded entity knowledge in plugin
11
+ */
12
+
13
+ import type { BaseMessage } from '@langchain/core/messages'
14
+ import type { AgentContext, SessionConfig, LLMProvider } from '../../types/langchain.types'
15
+ import type { z } from 'zod'
16
+
17
+ // ============================================
18
+ // AGENT TOOL INTERFACE (NEW - GENERIC)
19
+ // ============================================
20
+
21
+ /**
22
+ * Handler node function type
23
+ * Theme must implement these and pass to graph factory
24
+ */
25
+ export type HandlerNodeFn = (state: OrchestratorState) => Promise<Partial<OrchestratorState>>
26
+
27
+ /**
28
+ * Agent tool definition - describes a capability the orchestrator can use
29
+ *
30
+ * Example:
31
+ * {
32
+ * name: 'task',
33
+ * description: 'Task/todo management (list, create, update, delete)',
34
+ * handler: taskHandlerNode,
35
+ * exampleParameters: 'title, description, priority (low/medium/high/urgent)',
36
+ * }
37
+ */
38
+ export interface AgentTool {
39
+ /** Unique identifier (e.g., 'task', 'customer', 'product') */
40
+ name: string
41
+ /** Description for router prompt */
42
+ description: string
43
+ /** Optional Zod schema for parameter extraction */
44
+ parameterSchema?: z.ZodSchema
45
+ /** Handler implementation (theme-provided) */
46
+ handler: HandlerNodeFn
47
+ /** Example parameters for router prompt (optional) */
48
+ exampleParameters?: string
49
+ }
50
+
51
+ /**
52
+ * Orchestrator configuration - provided by theme
53
+ */
54
+ export interface OrchestratorConfig {
55
+ /** Tools registered by theme */
56
+ tools: AgentTool[]
57
+ /** System intents like 'greeting', 'clarification' (default: both) */
58
+ systemIntents?: ('greeting' | 'clarification')[]
59
+ /** Additional router prompt context (optional) */
60
+ routerPromptExtras?: string
61
+ }
62
+
63
+ // ============================================
64
+ // INTENT TYPES (NOW DYNAMIC)
65
+ // ============================================
66
+
67
+ /**
68
+ * System intent types (built into plugin)
69
+ */
70
+ export type SystemIntentType = 'greeting' | 'clarification'
71
+
72
+ /**
73
+ * Intent types that the router can classify
74
+ * Now dynamic - can be any tool name registered in OrchestratorConfig
75
+ */
76
+ export type IntentType = string
77
+
78
+ /**
79
+ * Actions that can be performed on entities
80
+ */
81
+ export type IntentAction = 'list' | 'create' | 'update' | 'delete' | 'search' | 'get' | 'unknown'
82
+
83
+ /**
84
+ * A single parsed intent from user input
85
+ */
86
+ export interface Intent {
87
+ /** Type of intent (which handler should process it) */
88
+ type: IntentType
89
+ /** Action to perform */
90
+ action: IntentAction
91
+ /** Extracted parameters for the action */
92
+ parameters: Record<string, unknown>
93
+ /** Original text that maps to this intent */
94
+ originalText: string
95
+ /** Confidence score from router (0-1) */
96
+ confidence?: number
97
+ }
98
+
99
+ /**
100
+ * Router output schema (for structured output)
101
+ */
102
+ export interface RouterOutput {
103
+ intents: Intent[]
104
+ /** If true, needs clarification from user */
105
+ needsClarification: boolean
106
+ /** Clarification question if needed */
107
+ clarificationQuestion?: string
108
+ }
109
+
110
+ // ============================================
111
+ // HANDLER RESULT TYPES (NOW GENERIC)
112
+ // ============================================
113
+
114
+ /**
115
+ * Generic handler result interface
116
+ * Replaces entity-specific types (TaskHandlerResult, CustomerHandlerResult, etc.)
117
+ */
118
+ export interface GenericHandlerResult {
119
+ success: boolean
120
+ operation: IntentAction
121
+ message: string
122
+ data: unknown
123
+ count?: number
124
+ error?: string
125
+ }
126
+
127
+ /**
128
+ * All handler results combined - now generic
129
+ * Keys are tool names, values are results
130
+ */
131
+ export type HandlerResults = Record<string, GenericHandlerResult>
132
+
133
+ // ============================================
134
+ // DEPRECATED TYPES (for backward compatibility during migration)
135
+ // ============================================
136
+
137
+ /**
138
+ * @deprecated Use GenericHandlerResult instead
139
+ */
140
+ export interface TaskHandlerResult extends GenericHandlerResult {
141
+ data: TaskData[] | TaskData | null
142
+ }
143
+
144
+ /**
145
+ * @deprecated Use generic data structure instead
146
+ */
147
+ export interface TaskData {
148
+ id: string
149
+ title: string
150
+ status?: string
151
+ priority?: string
152
+ dueDate?: string
153
+ description?: string
154
+ [key: string]: unknown
155
+ }
156
+
157
+ /**
158
+ * @deprecated Use GenericHandlerResult instead
159
+ */
160
+ export interface CustomerHandlerResult extends GenericHandlerResult {
161
+ data: CustomerData[] | CustomerData | null
162
+ }
163
+
164
+ /**
165
+ * @deprecated Use generic data structure instead
166
+ */
167
+ export interface CustomerData {
168
+ id: string
169
+ name: string
170
+ email?: string
171
+ phone?: string
172
+ accountNumber?: string
173
+ [key: string]: unknown
174
+ }
175
+
176
+ /**
177
+ * @deprecated Use GenericHandlerResult instead
178
+ */
179
+ export interface PageHandlerResult extends GenericHandlerResult {
180
+ data: PageData[] | PageData | null
181
+ }
182
+
183
+ /**
184
+ * @deprecated Use generic data structure instead
185
+ */
186
+ export interface PageData {
187
+ id: string
188
+ title: string
189
+ slug?: string
190
+ status?: string
191
+ [key: string]: unknown
192
+ }
193
+
194
+ // ============================================
195
+ // ORCHESTRATOR STATE
196
+ // ============================================
197
+
198
+ /**
199
+ * The main state that flows through the orchestrator graph
200
+ */
201
+ export interface OrchestratorState {
202
+ // ---- Input ----
203
+ /** Original user input */
204
+ input: string
205
+ /** Session identifier for memory */
206
+ sessionId: string
207
+ /** Multi-tenant context (userId, teamId, etc.) */
208
+ context: AgentContext
209
+ /** Recent conversation history (last N messages) */
210
+ conversationHistory: BaseMessage[]
211
+ /** Session configuration for memory */
212
+ sessionConfig?: SessionConfig
213
+
214
+ // ---- Router Configuration ----
215
+ /** Model configuration injected by theme (for router node) */
216
+ modelConfig?: ModelConfig
217
+
218
+ // ---- Router Output ----
219
+ /** Parsed intents from router */
220
+ intents: Intent[]
221
+ /** Whether clarification is needed */
222
+ needsClarification: boolean
223
+ /** Clarification question if needed */
224
+ clarificationQuestion?: string
225
+
226
+ // ---- Handler Outputs ----
227
+ /** Results from each handler (JSON) */
228
+ handlerResults: HandlerResults
229
+ /** Which handlers have been executed */
230
+ completedHandlers: IntentType[]
231
+
232
+ // ---- Final Output ----
233
+ /** Final response to send to user */
234
+ finalResponse: string | null
235
+ /** Error message if something failed */
236
+ error: string | null
237
+
238
+ // ---- Tracing ----
239
+ /** Trace ID for observability */
240
+ traceId?: string
241
+ /** Parent trace ID for nested calls */
242
+ parentTraceId?: string
243
+
244
+ // ---- Logging ----
245
+ /** Timestamp for logger filename (ensures all handlers write to same file) */
246
+ loggerTimestamp?: number
247
+ }
248
+
249
+ /**
250
+ * Initial state factory
251
+ */
252
+ export function createInitialState(
253
+ input: string,
254
+ sessionId: string,
255
+ context: AgentContext,
256
+ conversationHistory: BaseMessage[] = [],
257
+ sessionConfig?: SessionConfig
258
+ ): OrchestratorState {
259
+ return {
260
+ input,
261
+ sessionId,
262
+ context,
263
+ conversationHistory,
264
+ sessionConfig,
265
+ intents: [],
266
+ needsClarification: false,
267
+ handlerResults: {},
268
+ completedHandlers: [],
269
+ finalResponse: null,
270
+ error: null,
271
+ }
272
+ }
273
+
274
+ // ============================================
275
+ // GRAPH ROUTING TYPES (NOW DYNAMIC)
276
+ // ============================================
277
+
278
+ /**
279
+ * Possible routes from the router node - now dynamic based on tools
280
+ * Examples: 'task_handler', 'customer_handler', 'greeting', 'clarification', 'error'
281
+ */
282
+ export type RouterRoute = string
283
+
284
+ /**
285
+ * Possible routes after a handler completes - now dynamic
286
+ * Examples: 'customer_handler', 'page_handler', 'combiner'
287
+ */
288
+ export type PostHandlerRoute = string
289
+
290
+ // ============================================
291
+ // NODE FUNCTION TYPES
292
+ // ============================================
293
+
294
+ /**
295
+ * Type for graph node functions
296
+ */
297
+ export type GraphNode = (state: OrchestratorState) => Promise<Partial<OrchestratorState>>
298
+
299
+ /**
300
+ * Type for conditional edge functions
301
+ */
302
+ export type ConditionalEdge = (state: OrchestratorState) => RouterRoute | PostHandlerRoute
303
+
304
+ // ============================================
305
+ // CONFIGURATION
306
+ // ============================================
307
+
308
+ /**
309
+ * Graph configuration options
310
+ */
311
+ export interface GraphConfig {
312
+ /** Maximum conversation history messages to include */
313
+ maxHistoryMessages: number
314
+ /** Temperature for router LLM */
315
+ routerTemperature: number
316
+ /** Temperature for combiner LLM */
317
+ combinerTemperature: number
318
+ /** Whether to use parallel handler execution */
319
+ parallelExecution: boolean
320
+ /** Timeout for handler execution (ms) */
321
+ handlerTimeout: number
322
+ }
323
+
324
+ /**
325
+ * Default graph configuration
326
+ */
327
+ export const DEFAULT_GRAPH_CONFIG: GraphConfig = {
328
+ maxHistoryMessages: 5,
329
+ routerTemperature: 0.1,
330
+ combinerTemperature: 0.3,
331
+ parallelExecution: true,
332
+ handlerTimeout: 30000,
333
+ }
334
+
335
+ // ============================================
336
+ // HANDLER FACTORY TYPES (DEPRECATED)
337
+ // ============================================
338
+
339
+ /**
340
+ * @deprecated Use OrchestratorConfig.tools instead
341
+ * Handler factory configuration
342
+ * Theme provides implementations for these handlers
343
+ */
344
+ export interface HandlerFactories {
345
+ /** Handler for task-related operations */
346
+ taskHandler: HandlerNodeFn
347
+ /** Handler for customer-related operations */
348
+ customerHandler: HandlerNodeFn
349
+ /** Handler for page-related operations */
350
+ pageHandler: HandlerNodeFn
351
+ }
352
+
353
+ // ============================================
354
+ // MODEL CONFIGURATION TYPES
355
+ // ============================================
356
+
357
+ /**
358
+ * Model configuration to inject into state
359
+ * Avoids router needing to import from theme
360
+ */
361
+ export interface ModelConfig {
362
+ provider: LLMProvider
363
+ model?: string
364
+ temperature?: number
365
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Guardrails Service
3
+ *
4
+ * Security middleware for AI agents:
5
+ * - Prompt injection detection
6
+ * - PII masking
7
+ * - Content filtering
8
+ */
9
+
10
+ // Injection detection patterns
11
+ const INJECTION_PATTERNS = [
12
+ /ignore\s+(previous|all|above)\s+(instructions?|prompts?)/i,
13
+ /forget\s+(everything|all|previous)/i,
14
+ /you\s+are\s+now\s+/i,
15
+ /disregard\s+(all|previous|your)\s/i,
16
+ /pretend\s+(you\s+are|to\s+be)/i,
17
+ /act\s+as\s+(if|a|an)\s/i,
18
+ /jailbreak/i,
19
+ /bypass\s+(restrictions?|filters?|rules?)/i,
20
+ /system\s*:\s*/i, // Attempting to inject system prompt
21
+ /\[system\]/i,
22
+ /\<system\>/i,
23
+ /\{\{.*system.*\}\}/i, // Template injection
24
+ ]
25
+
26
+ // PII patterns
27
+ const PII_PATTERNS = {
28
+ email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
29
+ phone: /\b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
30
+ ssn: /\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g,
31
+ creditCard: /\b(?:\d{4}[-.\s]?){3}\d{4}\b/g,
32
+ ipAddress: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
33
+ }
34
+
35
+ // Content filter patterns (examples - should be customizable)
36
+ const CONTENT_FILTER_PATTERNS: RegExp[] = [
37
+ // Add patterns for harmful content
38
+ // These are placeholders - real implementation should be customizable
39
+ ]
40
+
41
+ export interface InjectionCheckResult {
42
+ safe: boolean
43
+ reason?: string
44
+ pattern?: string
45
+ }
46
+
47
+ export interface PIIMaskResult {
48
+ masked: string
49
+ mappings: Array<{ original: string; masked: string; type: string }>
50
+ hasPII: boolean
51
+ }
52
+
53
+ export interface ContentFilterResult {
54
+ filtered: string
55
+ blocked: boolean
56
+ reason?: string
57
+ }
58
+
59
+ export interface GuardrailsConfig {
60
+ promptInjection?: {
61
+ enabled: boolean
62
+ action: 'block' | 'warn' | 'log'
63
+ customPatterns?: RegExp[]
64
+ }
65
+ piiMasking?: {
66
+ enabled: boolean
67
+ types: Array<'email' | 'phone' | 'ssn' | 'creditCard' | 'ipAddress'>
68
+ action: 'mask' | 'remove' | 'log'
69
+ }
70
+ contentFilter?: {
71
+ enabled: boolean
72
+ customPatterns?: RegExp[]
73
+ action: 'block' | 'redact'
74
+ }
75
+ }
76
+
77
+ export const guardrails = {
78
+ /**
79
+ * Check input for prompt injection attempts
80
+ */
81
+ checkInjection(
82
+ input: string,
83
+ config?: GuardrailsConfig['promptInjection']
84
+ ): InjectionCheckResult {
85
+ if (!config?.enabled) {
86
+ return { safe: true }
87
+ }
88
+
89
+ const patterns = [
90
+ ...INJECTION_PATTERNS,
91
+ ...(config.customPatterns || []),
92
+ ]
93
+
94
+ for (const pattern of patterns) {
95
+ if (pattern.test(input)) {
96
+ return {
97
+ safe: false,
98
+ reason: 'Potential prompt injection detected',
99
+ pattern: pattern.toString(),
100
+ }
101
+ }
102
+ }
103
+
104
+ return { safe: true }
105
+ },
106
+
107
+ /**
108
+ * Mask PII in input text
109
+ */
110
+ maskPII(
111
+ input: string,
112
+ config?: GuardrailsConfig['piiMasking']
113
+ ): PIIMaskResult {
114
+ if (!config?.enabled) {
115
+ return { masked: input, mappings: [], hasPII: false }
116
+ }
117
+
118
+ let masked = input
119
+ const mappings: PIIMaskResult['mappings'] = []
120
+
121
+ for (const type of config.types) {
122
+ const pattern = PII_PATTERNS[type]
123
+ if (!pattern) continue
124
+
125
+ // Reset lastIndex for global patterns
126
+ pattern.lastIndex = 0
127
+
128
+ const matches = input.matchAll(new RegExp(pattern))
129
+ for (const match of matches) {
130
+ const original = match[0]
131
+ const maskChar = '*'
132
+ const maskedValue = config.action === 'remove'
133
+ ? '[REDACTED]'
134
+ : original.slice(0, 2) + maskChar.repeat(original.length - 4) + original.slice(-2)
135
+
136
+ mappings.push({ original, masked: maskedValue, type })
137
+ masked = masked.replace(original, maskedValue)
138
+ }
139
+ }
140
+
141
+ return {
142
+ masked,
143
+ mappings,
144
+ hasPII: mappings.length > 0,
145
+ }
146
+ },
147
+
148
+ /**
149
+ * Filter content from AI output
150
+ */
151
+ filterContent(
152
+ output: string,
153
+ config?: GuardrailsConfig['contentFilter']
154
+ ): ContentFilterResult {
155
+ if (!config?.enabled) {
156
+ return { filtered: output, blocked: false }
157
+ }
158
+
159
+ const patterns = [
160
+ ...CONTENT_FILTER_PATTERNS,
161
+ ...(config.customPatterns || []),
162
+ ]
163
+
164
+ for (const pattern of patterns) {
165
+ if (pattern.test(output)) {
166
+ if (config.action === 'block') {
167
+ return {
168
+ filtered: '',
169
+ blocked: true,
170
+ reason: 'Content blocked by filter',
171
+ }
172
+ } else {
173
+ // Redact matching content
174
+ output = output.replace(pattern, '[FILTERED]')
175
+ }
176
+ }
177
+ }
178
+
179
+ return { filtered: output, blocked: false }
180
+ },
181
+
182
+ /**
183
+ * Run all guardrails on input
184
+ * Returns processed input or throws if blocked
185
+ */
186
+ async processInput(
187
+ input: string,
188
+ config?: GuardrailsConfig
189
+ ): Promise<{ processed: string; warnings: string[] }> {
190
+ const warnings: string[] = []
191
+ let processed = input
192
+
193
+ // Check injection
194
+ if (config?.promptInjection?.enabled) {
195
+ const injectionResult = this.checkInjection(input, config.promptInjection)
196
+ if (!injectionResult.safe) {
197
+ if (config.promptInjection.action === 'block') {
198
+ throw new Error(`Input blocked: ${injectionResult.reason}`)
199
+ }
200
+ warnings.push(injectionResult.reason || 'Potential injection detected')
201
+ }
202
+ }
203
+
204
+ // Mask PII
205
+ if (config?.piiMasking?.enabled) {
206
+ const piiResult = this.maskPII(processed, config.piiMasking)
207
+ processed = piiResult.masked
208
+ if (piiResult.hasPII) {
209
+ warnings.push(`PII detected and masked: ${piiResult.mappings.length} items`)
210
+ }
211
+ }
212
+
213
+ return { processed, warnings }
214
+ },
215
+
216
+ /**
217
+ * Run content filter on output
218
+ */
219
+ async processOutput(
220
+ output: string,
221
+ config?: GuardrailsConfig
222
+ ): Promise<{ processed: string; blocked: boolean }> {
223
+ if (!config?.contentFilter?.enabled) {
224
+ return { processed: output, blocked: false }
225
+ }
226
+
227
+ const result = this.filterContent(output, config.contentFilter)
228
+ return { processed: result.filtered, blocked: result.blocked }
229
+ },
230
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,44 @@
1
+ // Agent
2
+ export { createAgent } from './agent-factory'
3
+
4
+ // Tools
5
+ export { createTool, buildTools, type ToolDefinition } from './tools-builder'
6
+
7
+ // Memory
8
+ export {
9
+ memoryStore,
10
+ dbMemoryStore,
11
+ type AgentContext,
12
+ type SessionConfig,
13
+ } from './memory-store'
14
+
15
+ // Prompt Renderer
16
+ export { renderPrompt, hasTemplateVariables, compilePrompt } from './prompt-renderer'
17
+
18
+ // Message Serialization
19
+ export {
20
+ serializeMessages,
21
+ deserializeMessages,
22
+ type SerializedMessage,
23
+ } from './message-serializer'
24
+
25
+ // Providers
26
+ export {
27
+ createOllamaModel,
28
+ createOpenAIModel,
29
+ createAnthropicModel,
30
+ getModel,
31
+ isProviderAvailable,
32
+ getAvailableProviders,
33
+ } from './providers'
34
+
35
+ // Types (re-export for convenience)
36
+ export type {
37
+ ModelConfig,
38
+ LLMProvider,
39
+ ThemeLangChainConfig,
40
+ AgentConfig,
41
+ AgentResponse,
42
+ ChatMessage,
43
+ ProviderConfig,
44
+ } from '../types/langchain.types'
package/lib/logger.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { FileLogger } from '@nextsparkjs/core/lib/utils/file-logger'
2
+
3
+ export interface AgentLoggerOptions {
4
+ /** Agent name (e.g., 'graph-orchestrator', 'single-agent') */
5
+ agentName: string
6
+ /** User display name (firstname-lastname), defaults to 'system' */
7
+ userName?: string
8
+ /** Session timestamp, defaults to Date.now() */
9
+ timestamp?: number
10
+ }
11
+
12
+ /**
13
+ * Sanitize a string for use in filenames.
14
+ * - Converts to lowercase
15
+ * - Replaces spaces with hyphens
16
+ * - Removes special characters except hyphens and underscores
17
+ * - Collapses multiple hyphens into one
18
+ */
19
+ function sanitizeForFilename(str: string): string {
20
+ return str
21
+ .toLowerCase()
22
+ .trim()
23
+ .replace(/\s+/g, '-') // spaces to hyphens
24
+ .replace(/[^a-z0-9\-_]/g, '') // remove special chars
25
+ .replace(/-+/g, '-') // collapse multiple hyphens
26
+ .replace(/^-|-$/g, '') // trim leading/trailing hyphens
27
+ || 'unknown' // fallback if empty
28
+ }
29
+
30
+ /**
31
+ * Create a logger for an AI agent session.
32
+ *
33
+ * Logs are stored in: logger/langchain/{agentName}/{userName}-{timestamp}.log
34
+ *
35
+ * The caller is responsible for managing the logger lifecycle.
36
+ * When the agent/session ends, the logger will be garbage collected.
37
+ *
38
+ * Logging is controlled by the LOG_ENABLED environment variable.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const logger = createAgentLogger({
43
+ * agentName: 'graph-orchestrator',
44
+ * userName: 'Carlos García', // → carlos-garcia
45
+ * })
46
+ *
47
+ * await logger.info('SESSION_INIT', { model: 'gpt-4' })
48
+ * await logger.info('USER_MESSAGE', { message })
49
+ * await logger.error('LLM_FAILED', { error })
50
+ * ```
51
+ */
52
+ export function createAgentLogger(options: AgentLoggerOptions): FileLogger {
53
+ const { agentName, userName = 'system', timestamp = Date.now() } = options
54
+ const sanitizedUserName = sanitizeForFilename(userName)
55
+
56
+ return new FileLogger({
57
+ folder: `langchain/${agentName}`,
58
+ filename: `${sanitizedUserName}-${timestamp}`,
59
+ })
60
+ }
61
+
62
+ /**
63
+ * Clear all LangChain agent logs
64
+ * @param agentName - Optional agent name to clear specific agent logs
65
+ * @returns Number of log files deleted
66
+ */
67
+ export async function clearAgentLogs(agentName?: string): Promise<number> {
68
+ const folder = agentName ? `langchain/${agentName}` : 'langchain'
69
+ return FileLogger.clearAll(folder)
70
+ }