agent-world 0.11.1 → 0.12.0
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/README.md +17 -7
- package/dist/cli/commands.d.ts +109 -0
- package/dist/cli/commands.js +2024 -0
- package/dist/cli/display.d.ts +124 -0
- package/dist/cli/display.js +381 -0
- package/dist/cli/hitl.d.ts +33 -0
- package/dist/cli/hitl.js +81 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/stream.d.ts +41 -0
- package/dist/cli/stream.js +222 -0
- package/dist/core/activity-tracker.d.ts +16 -0
- package/dist/core/activity-tracker.d.ts.map +1 -0
- package/dist/core/activity-tracker.js +91 -0
- package/dist/core/activity-tracker.js.map +1 -0
- package/dist/core/ai-commands.d.ts +16 -0
- package/dist/core/ai-commands.d.ts.map +1 -0
- package/dist/core/ai-commands.js +24 -0
- package/dist/core/ai-commands.js.map +1 -0
- package/dist/core/ai-sdk-patch.d.ts +24 -0
- package/dist/core/ai-sdk-patch.d.ts.map +1 -0
- package/dist/core/ai-sdk-patch.js +169 -0
- package/dist/core/ai-sdk-patch.js.map +1 -0
- package/dist/core/anthropic-direct.d.ts +52 -0
- package/dist/core/anthropic-direct.d.ts.map +1 -0
- package/dist/core/anthropic-direct.js +301 -0
- package/dist/core/anthropic-direct.js.map +1 -0
- package/dist/core/approval-cache.d.ts +104 -0
- package/dist/core/approval-cache.d.ts.map +1 -0
- package/dist/core/approval-cache.js +150 -0
- package/dist/core/approval-cache.js.map +1 -0
- package/dist/core/chat-constants.d.ts +20 -0
- package/dist/core/chat-constants.d.ts.map +1 -0
- package/dist/core/chat-constants.js +22 -0
- package/dist/core/chat-constants.js.map +1 -0
- package/dist/core/create-agent-tool.d.ts +66 -0
- package/dist/core/create-agent-tool.d.ts.map +1 -0
- package/dist/core/create-agent-tool.js +212 -0
- package/dist/core/create-agent-tool.js.map +1 -0
- package/dist/core/events/approval-checker.d.ts +61 -0
- package/dist/core/events/approval-checker.d.ts.map +1 -0
- package/dist/core/events/approval-checker.js +226 -0
- package/dist/core/events/approval-checker.js.map +1 -0
- package/dist/core/events/index.d.ts +25 -0
- package/dist/core/events/index.d.ts.map +1 -0
- package/dist/core/events/index.js +30 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/events/memory-manager.d.ts +73 -0
- package/dist/core/events/memory-manager.d.ts.map +1 -0
- package/dist/core/events/memory-manager.js +1218 -0
- package/dist/core/events/memory-manager.js.map +1 -0
- package/dist/core/events/mention-logic.d.ts +39 -0
- package/dist/core/events/mention-logic.d.ts.map +1 -0
- package/dist/core/events/mention-logic.js +163 -0
- package/dist/core/events/mention-logic.js.map +1 -0
- package/dist/core/events/orchestrator.d.ts +69 -0
- package/dist/core/events/orchestrator.d.ts.map +1 -0
- package/dist/core/events/orchestrator.js +883 -0
- package/dist/core/events/orchestrator.js.map +1 -0
- package/dist/core/events/persistence.d.ts +41 -0
- package/dist/core/events/persistence.d.ts.map +1 -0
- package/dist/core/events/persistence.js +296 -0
- package/dist/core/events/persistence.js.map +1 -0
- package/dist/core/events/publishers.d.ts +81 -0
- package/dist/core/events/publishers.d.ts.map +1 -0
- package/dist/core/events/publishers.js +272 -0
- package/dist/core/events/publishers.js.map +1 -0
- package/dist/core/events/subscribers.d.ts +45 -0
- package/dist/core/events/subscribers.d.ts.map +1 -0
- package/dist/core/events/subscribers.js +288 -0
- package/dist/core/events/subscribers.js.map +1 -0
- package/dist/core/events/tool-bridge-logging.d.ts +28 -0
- package/dist/core/events/tool-bridge-logging.d.ts.map +1 -0
- package/dist/core/events/tool-bridge-logging.js +94 -0
- package/dist/core/events/tool-bridge-logging.js.map +1 -0
- package/dist/core/events-metadata.d.ts +72 -0
- package/dist/core/events-metadata.d.ts.map +1 -0
- package/dist/core/events-metadata.js +167 -0
- package/dist/core/events-metadata.js.map +1 -0
- package/dist/core/events.d.ts +186 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +1248 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/export.d.ts +106 -0
- package/dist/core/export.d.ts.map +1 -0
- package/dist/core/export.js +705 -0
- package/dist/core/export.js.map +1 -0
- package/dist/core/file-tools.d.ts +114 -0
- package/dist/core/file-tools.d.ts.map +1 -0
- package/dist/core/file-tools.js +370 -0
- package/dist/core/file-tools.js.map +1 -0
- package/dist/core/google-direct.d.ts +58 -0
- package/dist/core/google-direct.d.ts.map +1 -0
- package/dist/core/google-direct.js +298 -0
- package/dist/core/google-direct.js.map +1 -0
- package/dist/core/hitl.d.ts +54 -0
- package/dist/core/hitl.d.ts.map +1 -0
- package/dist/core/hitl.js +153 -0
- package/dist/core/hitl.js.map +1 -0
- package/dist/core/index.d.ts +59 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +70 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/llm-config.d.ts +128 -0
- package/dist/core/llm-config.d.ts.map +1 -0
- package/dist/core/llm-config.js +164 -0
- package/dist/core/llm-config.js.map +1 -0
- package/dist/core/llm-manager.d.ts +163 -0
- package/dist/core/llm-manager.d.ts.map +1 -0
- package/dist/core/llm-manager.js +669 -0
- package/dist/core/llm-manager.js.map +1 -0
- package/dist/core/load-skill-tool.d.ts +55 -0
- package/dist/core/load-skill-tool.d.ts.map +1 -0
- package/dist/core/load-skill-tool.js +468 -0
- package/dist/core/load-skill-tool.js.map +1 -0
- package/dist/core/logger.d.ts +88 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +358 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/managers.d.ts +131 -0
- package/dist/core/managers.d.ts.map +1 -0
- package/dist/core/managers.js +1223 -0
- package/dist/core/managers.js.map +1 -0
- package/dist/core/mcp-server-registry.d.ts +304 -0
- package/dist/core/mcp-server-registry.d.ts.map +1 -0
- package/dist/core/mcp-server-registry.js +1769 -0
- package/dist/core/mcp-server-registry.js.map +1 -0
- package/dist/core/mcp-tools.d.ts +56 -0
- package/dist/core/mcp-tools.d.ts.map +1 -0
- package/dist/core/mcp-tools.js +186 -0
- package/dist/core/mcp-tools.js.map +1 -0
- package/dist/core/message-prep.d.ts +81 -0
- package/dist/core/message-prep.d.ts.map +1 -0
- package/dist/core/message-prep.js +223 -0
- package/dist/core/message-prep.js.map +1 -0
- package/dist/core/message-processing-control.d.ts +54 -0
- package/dist/core/message-processing-control.d.ts.map +1 -0
- package/dist/core/message-processing-control.js +139 -0
- package/dist/core/message-processing-control.js.map +1 -0
- package/dist/core/openai-direct.d.ts +80 -0
- package/dist/core/openai-direct.d.ts.map +1 -0
- package/dist/core/openai-direct.js +374 -0
- package/dist/core/openai-direct.js.map +1 -0
- package/dist/core/shell-cmd-tool.d.ts +235 -0
- package/dist/core/shell-cmd-tool.d.ts.map +1 -0
- package/dist/core/shell-cmd-tool.js +1157 -0
- package/dist/core/shell-cmd-tool.js.map +1 -0
- package/dist/core/shell-process-registry.d.ts +88 -0
- package/dist/core/shell-process-registry.d.ts.map +1 -0
- package/dist/core/shell-process-registry.js +309 -0
- package/dist/core/shell-process-registry.js.map +1 -0
- package/dist/core/skill-registry.d.ts +75 -0
- package/dist/core/skill-registry.d.ts.map +1 -0
- package/dist/core/skill-registry.js +369 -0
- package/dist/core/skill-registry.js.map +1 -0
- package/dist/core/skill-script-runner.d.ts +89 -0
- package/dist/core/skill-script-runner.d.ts.map +1 -0
- package/dist/core/skill-script-runner.js +274 -0
- package/dist/core/skill-script-runner.js.map +1 -0
- package/dist/core/skill-selector.d.ts +65 -0
- package/dist/core/skill-selector.d.ts.map +1 -0
- package/dist/core/skill-selector.js +190 -0
- package/dist/core/skill-selector.js.map +1 -0
- package/dist/core/skill-settings.d.ts +20 -0
- package/dist/core/skill-settings.d.ts.map +1 -0
- package/dist/core/skill-settings.js +40 -0
- package/dist/core/skill-settings.js.map +1 -0
- package/dist/core/storage/agent-storage.d.ts +134 -0
- package/dist/core/storage/agent-storage.d.ts.map +1 -0
- package/dist/core/storage/agent-storage.js +498 -0
- package/dist/core/storage/agent-storage.js.map +1 -0
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts +100 -0
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/fileEventStorage.js +494 -0
- package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/index.d.ts +31 -0
- package/dist/core/storage/eventStorage/index.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/index.js +31 -0
- package/dist/core/storage/eventStorage/index.js.map +1 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +87 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.js +244 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +45 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.js +301 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/types.d.ts +142 -0
- package/dist/core/storage/eventStorage/types.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/types.js +43 -0
- package/dist/core/storage/eventStorage/types.js.map +1 -0
- package/dist/core/storage/eventStorage/validation.d.ts +30 -0
- package/dist/core/storage/eventStorage/validation.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/validation.js +68 -0
- package/dist/core/storage/eventStorage/validation.js.map +1 -0
- package/dist/core/storage/legacy-migrations.d.ts +45 -0
- package/dist/core/storage/legacy-migrations.d.ts.map +1 -0
- package/dist/core/storage/legacy-migrations.js +295 -0
- package/dist/core/storage/legacy-migrations.js.map +1 -0
- package/dist/core/storage/memory-storage.d.ts +105 -0
- package/dist/core/storage/memory-storage.d.ts.map +1 -0
- package/dist/core/storage/memory-storage.js +415 -0
- package/dist/core/storage/memory-storage.js.map +1 -0
- package/dist/core/storage/migration-runner.d.ts +96 -0
- package/dist/core/storage/migration-runner.d.ts.map +1 -0
- package/dist/core/storage/migration-runner.js +306 -0
- package/dist/core/storage/migration-runner.js.map +1 -0
- package/dist/core/storage/queue-storage.d.ts +147 -0
- package/dist/core/storage/queue-storage.d.ts.map +1 -0
- package/dist/core/storage/queue-storage.js +290 -0
- package/dist/core/storage/queue-storage.js.map +1 -0
- package/dist/core/storage/skill-storage.d.ts +136 -0
- package/dist/core/storage/skill-storage.d.ts.map +1 -0
- package/dist/core/storage/skill-storage.js +474 -0
- package/dist/core/storage/skill-storage.js.map +1 -0
- package/dist/core/storage/sqlite-schema.d.ts +95 -0
- package/dist/core/storage/sqlite-schema.d.ts.map +1 -0
- package/dist/core/storage/sqlite-schema.js +156 -0
- package/dist/core/storage/sqlite-schema.js.map +1 -0
- package/dist/core/storage/sqlite-storage.d.ts +146 -0
- package/dist/core/storage/sqlite-storage.d.ts.map +1 -0
- package/dist/core/storage/sqlite-storage.js +709 -0
- package/dist/core/storage/sqlite-storage.js.map +1 -0
- package/dist/core/storage/storage-factory.d.ts +61 -0
- package/dist/core/storage/storage-factory.d.ts.map +1 -0
- package/dist/core/storage/storage-factory.js +794 -0
- package/dist/core/storage/storage-factory.js.map +1 -0
- package/dist/core/storage/validation.d.ts +36 -0
- package/dist/core/storage/validation.d.ts.map +1 -0
- package/dist/core/storage/validation.js +79 -0
- package/dist/core/storage/validation.js.map +1 -0
- package/dist/core/storage/world-storage.d.ts +114 -0
- package/dist/core/storage/world-storage.d.ts.map +1 -0
- package/dist/core/storage/world-storage.js +378 -0
- package/dist/core/storage/world-storage.js.map +1 -0
- package/dist/core/subscription.d.ts +43 -0
- package/dist/core/subscription.d.ts.map +1 -0
- package/dist/core/subscription.js +227 -0
- package/dist/core/subscription.js.map +1 -0
- package/dist/core/tool-utils.d.ts +80 -0
- package/dist/core/tool-utils.d.ts.map +1 -0
- package/dist/core/tool-utils.js +273 -0
- package/dist/core/tool-utils.js.map +1 -0
- package/dist/core/types.d.ts +595 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +158 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/utils.d.ts +138 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +478 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/core/world-class.d.ts +43 -0
- package/dist/core/world-class.d.ts.map +1 -0
- package/dist/core/world-class.js +90 -0
- package/dist/core/world-class.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/public/assets/agent-sprites-DJFgj-zP.png +0 -0
- package/dist/public/assets/border-KHK37r8y.svg +83 -0
- package/dist/public/assets/index-C9kPXL6G.css +1 -0
- package/dist/public/assets/index-DOQEHGWt.js +96 -0
- package/dist/public/index.html +21 -0
- package/dist/server/api.d.ts +2 -0
- package/dist/server/api.js +1124 -0
- package/dist/server/index.d.ts +29 -0
- package/dist/server/sse-handler.d.ts +62 -0
- package/dist/server/sse-handler.js +234 -0
- package/package.json +15 -3
- package/scripts/launch-electron.js +0 -58
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Manager Module - Pure Orchestration Layer (LLM Provider Refactoring Phase 5)
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Browser-safe LLM integration using direct provider SDKs (OpenAI, Anthropic, Google)
|
|
6
|
+
* - Streaming responses with SSE events via World.eventEmitter specifically
|
|
7
|
+
* - Support for all major LLM providers (OpenAI, Anthropic, Google, Azure, XAI, OpenAI-Compatible, Ollama)
|
|
8
|
+
* - Agent activity tracking and token usage monitoring with automatic state persistence
|
|
9
|
+
* - Error handling with SSE error events via world's eventEmitter and timeout management
|
|
10
|
+
* - World-aware event publishing using world.eventEmitter for proper event isolation
|
|
11
|
+
* - Conversation history support with message preparation and context management
|
|
12
|
+
* - Global LLM call queue to ensure serialized execution (one LLM call at a time)
|
|
13
|
+
* - Configuration injection from external sources (CLI/server) for browser compatibility
|
|
14
|
+
* - Automatic MCP tool integration for worlds with mcpConfig
|
|
15
|
+
* - All providers return LLMResponse with unified structure
|
|
16
|
+
* - Granular function-based logging for detailed debugging control
|
|
17
|
+
*
|
|
18
|
+
* Core Functions:
|
|
19
|
+
* - streamAgentResponse: Streaming LLM calls with SSE events via world.eventEmitter (queued)
|
|
20
|
+
* - generateAgentResponse: Non-streaming LLM calls with automatic state management (queued)
|
|
21
|
+
* - loadLLMProvider: Provider loading logic using injected configuration
|
|
22
|
+
* - getLLMQueueStatus: Monitor queue status for debugging and administration
|
|
23
|
+
* - clearLLMQueue: Emergency queue clearing for administrative purposes
|
|
24
|
+
*
|
|
25
|
+
* Provider Support:
|
|
26
|
+
* - OpenAI: Direct OpenAI package integration (bypasses AI SDK bug)
|
|
27
|
+
* - Azure: Direct OpenAI package integration with Azure endpoints (bypasses AI SDK bug)
|
|
28
|
+
* - OpenAI-Compatible: Direct OpenAI package integration (bypasses AI SDK bug)
|
|
29
|
+
* - XAI: Direct OpenAI package integration with XAI endpoints (bypasses AI SDK bug)
|
|
30
|
+
* - Ollama: Direct OpenAI package integration with OpenAI-compatible endpoint (better function calling)
|
|
31
|
+
* - Anthropic: Direct Anthropic SDK integration (improved tool calling support)
|
|
32
|
+
* - Google: Direct Google Generative AI SDK integration (improved tool calling support)
|
|
33
|
+
*
|
|
34
|
+
* Granular Logging Categories:
|
|
35
|
+
* - llm.queue: Queue operations (add, process, complete, errors)
|
|
36
|
+
* - llm.streaming: Streaming response operations (start, chunks, finish, errors)
|
|
37
|
+
* - llm.generation: Non-streaming response operations (start, finish, errors)
|
|
38
|
+
* - llm.provider: Provider loading, configuration, and validation
|
|
39
|
+
* - llm.mcp: Comprehensive MCP tool integration and execution tracking
|
|
40
|
+
* - llm.util: Utility functions and helper operations
|
|
41
|
+
*
|
|
42
|
+
* Environment Variable Control:
|
|
43
|
+
* - LOG_LLM_QUEUE=debug: Enable queue operation debugging
|
|
44
|
+
* - LOG_LLM_STREAMING=debug: Enable streaming operation debugging
|
|
45
|
+
* - LOG_LLM_GENERATION=debug: Enable generation operation debugging
|
|
46
|
+
* - LOG_LLM_PROVIDER=debug: Enable provider operation debugging
|
|
47
|
+
* - LOG_LLM_MCP=debug: Enable comprehensive MCP tool debugging (consolidates all MCP logging)
|
|
48
|
+
* - LOG_LLM_UTIL=debug: Enable utility function debugging
|
|
49
|
+
*
|
|
50
|
+
* MCP Tool Logging Features (LOG_LLM_MCP=debug):
|
|
51
|
+
* - Tool call sequence tracking with unique sequence IDs
|
|
52
|
+
* - Tool execution performance metrics (duration in milliseconds)
|
|
53
|
+
* - Tool result content analysis (size, type, preview)
|
|
54
|
+
* - Tool call success/failure status with detailed error information
|
|
55
|
+
* - Tool call dependencies and parent-child relationships
|
|
56
|
+
* - Tool argument validation and presence checking
|
|
57
|
+
* - Streaming vs non-streaming execution path differentiation
|
|
58
|
+
* - Complete tool call lifecycle from start to completion
|
|
59
|
+
* - Server-side tool execution via direct MCP server registry calls
|
|
60
|
+
* - AI SDK tool conversion execution tracking
|
|
61
|
+
* - Tool result processing and content type identification
|
|
62
|
+
*
|
|
63
|
+
* LLM Queue Implementation:
|
|
64
|
+
* - Global singleton queue prevents concurrent LLM calls across all agents and worlds
|
|
65
|
+
* - FIFO (First In, First Out) processing ensures fair agent response ordering
|
|
66
|
+
* - Maximum queue size of 100 items prevents memory overflow issues
|
|
67
|
+
* - 15-minute timeout per LLM call supports long-running tool executions (configurable)
|
|
68
|
+
* - Warning logs at 50% timeout threshold for debugging long-running operations
|
|
69
|
+
* - Queue status monitoring available for debugging and performance analysis
|
|
70
|
+
* - Emergency clear function allows administrative queue reset when needed
|
|
71
|
+
* - Proper error handling with promise rejection for failed calls
|
|
72
|
+
* - Automatic queue processing with safety measures for edge cases
|
|
73
|
+
* - Timeout cleanup on promise resolution prevents resource leaks and Jest hanging
|
|
74
|
+
* - Configurable timeout via setProcessingTimeout() for different use cases
|
|
75
|
+
*
|
|
76
|
+
* Browser Safety Implementation:
|
|
77
|
+
* - Zero process.env dependencies for browser compatibility
|
|
78
|
+
* - Configuration injection via llm-config module
|
|
79
|
+
* - All provider settings supplied externally by CLI/server components
|
|
80
|
+
* - Type-safe configuration interfaces prevent runtime errors
|
|
81
|
+
* - Clear error messages when configuration is missing
|
|
82
|
+
*
|
|
83
|
+
* Implementation Details:
|
|
84
|
+
* - Uses direct OpenAI package for OpenAI providers to avoid AI SDK schema corruption bug
|
|
85
|
+
* - Uses direct Anthropic SDK for Anthropic provider to fix tool calling issues
|
|
86
|
+
* - Uses direct Google Generative AI SDK for Google provider to fix tool calling issues
|
|
87
|
+
* - Publishes SSE events via world.eventEmitter.emit('sse', event) for proper isolation
|
|
88
|
+
* - Updates agent activity metrics and LLM call counts automatically
|
|
89
|
+
* - Zero dependencies on Node.js environment variables or legacy event systems
|
|
90
|
+
* - Complete provider support with externally injected configuration
|
|
91
|
+
* - All events scoped to specific world instance preventing cross-world interference
|
|
92
|
+
* - Full LLM provider support with configuration validation and error handling
|
|
93
|
+
* - Timeout handling with configurable limits and proper error recovery
|
|
94
|
+
* - Queue-based serialization prevents API rate limits and resource conflicts
|
|
95
|
+
*
|
|
96
|
+
* Recent Changes:
|
|
97
|
+
* - 2026-02-13: Reclassified stop-triggered aborts as cancellation/info logs (not errors) in queue and non-streaming paths.
|
|
98
|
+
* - 2026-02-13: Added merged external+queue abort-signal support so chat stop requests can cancel follow-up continuation calls.
|
|
99
|
+
* - 2026-02-13: Added chat-scoped LLM cancellation controls so Electron stop requests can abort active and queued calls by `worldId` + `chatId`.
|
|
100
|
+
* - 2026-02-08: Removed stale manual tool-intervention terminology from internal comments
|
|
101
|
+
* - 2025-11-09: Phase 5 - Updated to expect LLMResponse from all providers
|
|
102
|
+
* - Removed old manual tool decision return type handling
|
|
103
|
+
* - All providers now return unified LLMResponse interface with type discriminator
|
|
104
|
+
* - Updated logging to handle LLMResponse structure (type, content, tool_calls)
|
|
105
|
+
* - Providers are now pure clients - no tool execution, only API calls
|
|
106
|
+
* - NO type checking for string vs object - always LLMResponse
|
|
107
|
+
* - Tool orchestration will be handled by events.ts (Phase 6)
|
|
108
|
+
* - Simplified tool usage guidance: minimal system prompt patch for tool availability
|
|
109
|
+
* - Increased LLM queue timeout from 2 minutes to 15 minutes for long-running tool executions
|
|
110
|
+
* - Replaced AI SDK with direct OpenAI, Anthropic, and Google integrations
|
|
111
|
+
* - Implemented granular function-based logging for detailed debugging control
|
|
112
|
+
* - Tool-specific guidance moved to individual tool descriptions (proper separation)
|
|
113
|
+
*/
|
|
114
|
+
import { LLMProvider } from './types.js';
|
|
115
|
+
import { getMCPToolsForWorld } from './mcp-server-registry.js';
|
|
116
|
+
import { filterClientSideMessages } from './message-prep.js';
|
|
117
|
+
import { createClientForProvider, streamOpenAIResponse, generateOpenAIResponse } from './openai-direct.js';
|
|
118
|
+
import { createAnthropicClientForAgent, streamAnthropicResponse, generateAnthropicResponse } from './anthropic-direct.js';
|
|
119
|
+
import { createGoogleClientForAgent, streamGoogleResponse, generateGoogleResponse } from './google-direct.js';
|
|
120
|
+
import { generateId } from './utils.js';
|
|
121
|
+
import { createCategoryLogger } from './logger.js';
|
|
122
|
+
import { createStorageWithWrappers } from './storage/storage-factory.js';
|
|
123
|
+
// Granular function-specific loggers for detailed debugging control
|
|
124
|
+
const loggerQueue = createCategoryLogger('llm.queue');
|
|
125
|
+
const loggerStreaming = createCategoryLogger('llm.streaming');
|
|
126
|
+
const loggerGeneration = createCategoryLogger('llm.generation');
|
|
127
|
+
const loggerProvider = createCategoryLogger('llm.provider');
|
|
128
|
+
const loggerMCP = createCategoryLogger('llm.mcp');
|
|
129
|
+
const loggerUtil = createCategoryLogger('llm.util');
|
|
130
|
+
import { getLLMProviderConfig } from './llm-config.js';
|
|
131
|
+
// LLM Integration Utilities
|
|
132
|
+
function stripCustomFields(message) {
|
|
133
|
+
const { sender, chatId, ...llmMessage } = message;
|
|
134
|
+
loggerUtil.trace('Stripped custom fields from message', { originalFields: ['sender', 'chatId'], remainingKeys: Object.keys(llmMessage) });
|
|
135
|
+
return llmMessage;
|
|
136
|
+
}
|
|
137
|
+
function stripCustomFieldsFromMessages(messages) {
|
|
138
|
+
loggerUtil.debug(`Stripping custom fields from ${messages.length} messages`);
|
|
139
|
+
// First, filter out client-side tool request wrappers and orphaned tool results.
|
|
140
|
+
const filteredMessages = filterClientSideMessages(messages);
|
|
141
|
+
loggerUtil.debug(`Filtered to ${filteredMessages.length} messages (removed ${messages.length - filteredMessages.length} client-side messages)`);
|
|
142
|
+
// Then strip custom fields
|
|
143
|
+
return filteredMessages.map(stripCustomFields);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Append tool usage guidance to system message when tools are available
|
|
147
|
+
* Returns a new array with updated system message (doesn't mutate original)
|
|
148
|
+
*/
|
|
149
|
+
function appendToolRulesToSystemMessage(messages, hasMCPTools) {
|
|
150
|
+
if (!hasMCPTools || messages.length === 0 || messages[0].role !== 'system') {
|
|
151
|
+
return messages;
|
|
152
|
+
}
|
|
153
|
+
const systemMessage = messages[0];
|
|
154
|
+
// Simple guidance: Only use tools when user explicitly requests an action
|
|
155
|
+
const toolRules = '\n\nYou have access to tools. Use them only when the user explicitly requests an action.';
|
|
156
|
+
return [
|
|
157
|
+
{ ...systemMessage, content: systemMessage.content + toolRules },
|
|
158
|
+
...messages.slice(1)
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
// Storage wrapper for tool-execution follow-up handling
|
|
162
|
+
let storageWrappersPromise = null;
|
|
163
|
+
async function getStorageWrappers() {
|
|
164
|
+
if (!storageWrappersPromise) {
|
|
165
|
+
storageWrappersPromise = createStorageWithWrappers();
|
|
166
|
+
}
|
|
167
|
+
return storageWrappersPromise;
|
|
168
|
+
}
|
|
169
|
+
function normalizeChatId(chatId) {
|
|
170
|
+
if (chatId == null)
|
|
171
|
+
return '__none__';
|
|
172
|
+
return String(chatId);
|
|
173
|
+
}
|
|
174
|
+
class LLMQueue {
|
|
175
|
+
queue = [];
|
|
176
|
+
processing = false;
|
|
177
|
+
activeItem = null;
|
|
178
|
+
maxQueueSize = 100; // Prevent memory issues
|
|
179
|
+
processingTimeoutMs = 900000; // 15 minute max processing time per call (for long-running tools)
|
|
180
|
+
async add(agentId, worldId, chatId, task) {
|
|
181
|
+
// Prevent queue overflow
|
|
182
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
183
|
+
throw new Error(`LLM queue is full (${this.maxQueueSize} items). Please try again later.`);
|
|
184
|
+
}
|
|
185
|
+
loggerQueue.debug(`LLMQueue: Adding task for agent=${agentId}, world=${worldId}, chat=${normalizeChatId(chatId)}. Queue length before add: ${this.queue.length}`);
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const queueItem = {
|
|
188
|
+
id: generateId(),
|
|
189
|
+
agentId,
|
|
190
|
+
worldId,
|
|
191
|
+
chatId,
|
|
192
|
+
abortController: new AbortController(),
|
|
193
|
+
canceled: false,
|
|
194
|
+
execute: task,
|
|
195
|
+
resolve,
|
|
196
|
+
reject
|
|
197
|
+
};
|
|
198
|
+
this.queue.push(queueItem);
|
|
199
|
+
this.processQueue();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async processQueue() {
|
|
203
|
+
if (this.processing || this.queue.length === 0) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
this.processing = true;
|
|
207
|
+
loggerQueue.debug(`LLMQueue: Starting queue processing. Queue length: ${this.queue.length}`);
|
|
208
|
+
while (this.queue.length > 0) {
|
|
209
|
+
const item = this.queue.shift();
|
|
210
|
+
if (item.canceled) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
this.activeItem = item;
|
|
215
|
+
const taskStartTime = Date.now();
|
|
216
|
+
loggerQueue.debug(`LLMQueue: Processing task for agent=${item.agentId}, world=${item.worldId}, chat=${normalizeChatId(item.chatId)}, queueItemId=${item.id}`);
|
|
217
|
+
// Add processing timeout to prevent stuck queue
|
|
218
|
+
const processPromise = item.execute(item.abortController.signal);
|
|
219
|
+
// Store timeout ID so we can cancel it if process completes first
|
|
220
|
+
let timeoutId;
|
|
221
|
+
let warningTimeoutId;
|
|
222
|
+
// Warn if processing takes more than 50% of timeout
|
|
223
|
+
const warningThreshold = this.processingTimeoutMs * 0.5;
|
|
224
|
+
warningTimeoutId = setTimeout(() => {
|
|
225
|
+
const elapsed = Date.now() - taskStartTime;
|
|
226
|
+
loggerQueue.warn(`LLM task is taking longer than expected`, {
|
|
227
|
+
agentId: item.agentId,
|
|
228
|
+
worldId: item.worldId,
|
|
229
|
+
elapsed,
|
|
230
|
+
timeoutMs: this.processingTimeoutMs,
|
|
231
|
+
percentComplete: Math.round((elapsed / this.processingTimeoutMs) * 100)
|
|
232
|
+
});
|
|
233
|
+
}, warningThreshold);
|
|
234
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
235
|
+
timeoutId = setTimeout(() => {
|
|
236
|
+
reject(new Error(`LLM call timeout after ${this.processingTimeoutMs}ms for agent ${item.agentId}`));
|
|
237
|
+
}, this.processingTimeoutMs);
|
|
238
|
+
});
|
|
239
|
+
const result = await Promise.race([processPromise, timeoutPromise]);
|
|
240
|
+
// Clear both timeouts to prevent Jest from hanging
|
|
241
|
+
clearTimeout(timeoutId);
|
|
242
|
+
clearTimeout(warningTimeoutId);
|
|
243
|
+
item.resolve(result);
|
|
244
|
+
loggerQueue.debug(`LLMQueue: Finished processing task for agent=${item.agentId}, world=${item.worldId}, queueItemId=${item.id}`);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
const wasCanceled = item.canceled || item.abortController.signal.aborted || isAbortError(error);
|
|
248
|
+
if (wasCanceled) {
|
|
249
|
+
loggerQueue.info('LLM queue call canceled', {
|
|
250
|
+
agentId: item.agentId,
|
|
251
|
+
worldId: item.worldId,
|
|
252
|
+
chatId: normalizeChatId(item.chatId),
|
|
253
|
+
queueItemId: item.id,
|
|
254
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
loggerQueue.error('LLM queue error', {
|
|
259
|
+
agentId: item.agentId,
|
|
260
|
+
worldId: item.worldId,
|
|
261
|
+
chatId: normalizeChatId(item.chatId),
|
|
262
|
+
queueItemId: item.id,
|
|
263
|
+
error: error instanceof Error ? error.message : error
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
item.reject(error);
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
this.activeItem = null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
this.processing = false;
|
|
273
|
+
loggerQueue.debug('LLMQueue: Queue processing complete.');
|
|
274
|
+
}
|
|
275
|
+
getQueueStatus() {
|
|
276
|
+
const next = this.queue[0];
|
|
277
|
+
return {
|
|
278
|
+
queueLength: this.queue.length,
|
|
279
|
+
processing: this.processing,
|
|
280
|
+
nextAgent: next?.agentId,
|
|
281
|
+
nextWorld: next?.worldId,
|
|
282
|
+
maxQueueSize: this.maxQueueSize
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
// Emergency method to clear stuck queue (for debugging/admin use)
|
|
286
|
+
clearQueue() {
|
|
287
|
+
const clearedCount = this.queue.length;
|
|
288
|
+
for (const item of this.queue) {
|
|
289
|
+
item.canceled = true;
|
|
290
|
+
item.abortController.abort();
|
|
291
|
+
item.reject(new Error('LLM queue item canceled by queue clear.'));
|
|
292
|
+
}
|
|
293
|
+
this.queue.length = 0;
|
|
294
|
+
loggerQueue.info('LLM queue cleared', { clearedCount });
|
|
295
|
+
return clearedCount;
|
|
296
|
+
}
|
|
297
|
+
cancelByChat(worldId, chatId) {
|
|
298
|
+
const targetChatId = normalizeChatId(chatId);
|
|
299
|
+
let canceledPending = 0;
|
|
300
|
+
let abortedActive = 0;
|
|
301
|
+
this.queue = this.queue.filter((item) => {
|
|
302
|
+
const matchesWorld = item.worldId === worldId;
|
|
303
|
+
const matchesChat = normalizeChatId(item.chatId) === targetChatId;
|
|
304
|
+
if (!matchesWorld || !matchesChat) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
item.canceled = true;
|
|
308
|
+
item.abortController.abort();
|
|
309
|
+
item.reject(new Error(`LLM call canceled for world '${worldId}' chat '${targetChatId}'.`));
|
|
310
|
+
canceledPending += 1;
|
|
311
|
+
return false;
|
|
312
|
+
});
|
|
313
|
+
if (this.activeItem) {
|
|
314
|
+
const matchesWorld = this.activeItem.worldId === worldId;
|
|
315
|
+
const matchesChat = normalizeChatId(this.activeItem.chatId) === targetChatId;
|
|
316
|
+
if (matchesWorld && matchesChat && !this.activeItem.abortController.signal.aborted) {
|
|
317
|
+
this.activeItem.abortController.abort();
|
|
318
|
+
abortedActive = 1;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { canceledPending, abortedActive };
|
|
322
|
+
}
|
|
323
|
+
// Set processing timeout (useful for testing or adjusting for long-running operations)
|
|
324
|
+
setProcessingTimeout(timeoutMs) {
|
|
325
|
+
if (timeoutMs < 1000) {
|
|
326
|
+
throw new Error('Processing timeout must be at least 1000ms');
|
|
327
|
+
}
|
|
328
|
+
this.processingTimeoutMs = timeoutMs;
|
|
329
|
+
loggerQueue.info('LLM queue processing timeout updated', { timeoutMs });
|
|
330
|
+
}
|
|
331
|
+
// Get current processing timeout
|
|
332
|
+
getProcessingTimeout() {
|
|
333
|
+
return this.processingTimeoutMs;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Global singleton queue instance
|
|
337
|
+
const llmQueue = new LLMQueue();
|
|
338
|
+
function isAbortError(error) {
|
|
339
|
+
if (!error)
|
|
340
|
+
return false;
|
|
341
|
+
if (error instanceof DOMException && error.name === 'AbortError')
|
|
342
|
+
return true;
|
|
343
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
344
|
+
return true;
|
|
345
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
346
|
+
return message.toLowerCase().includes('abort');
|
|
347
|
+
}
|
|
348
|
+
function createCombinedAbortSignal(first, second) {
|
|
349
|
+
const signals = [first, second].filter((value) => Boolean(value));
|
|
350
|
+
if (signals.length === 0) {
|
|
351
|
+
return { signal: undefined, dispose: () => { } };
|
|
352
|
+
}
|
|
353
|
+
if (signals.length === 1) {
|
|
354
|
+
return { signal: signals[0], dispose: () => { } };
|
|
355
|
+
}
|
|
356
|
+
const controller = new AbortController();
|
|
357
|
+
const onAbort = () => {
|
|
358
|
+
if (!controller.signal.aborted) {
|
|
359
|
+
controller.abort();
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
for (const signal of signals) {
|
|
363
|
+
if (signal.aborted) {
|
|
364
|
+
controller.abort();
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
signal.addEventListener('abort', onAbort);
|
|
368
|
+
}
|
|
369
|
+
const dispose = () => {
|
|
370
|
+
for (const signal of signals) {
|
|
371
|
+
signal.removeEventListener('abort', onAbort);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
return { signal: controller.signal, dispose };
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Streaming agent response with SSE events via world's eventEmitter (queued)
|
|
378
|
+
*/
|
|
379
|
+
export async function streamAgentResponse(world, agent, messages, publishSSE, chatId = null, abortSignal) {
|
|
380
|
+
if (abortSignal?.aborted) {
|
|
381
|
+
throw new DOMException(`LLM call aborted before queue for agent ${agent.id}`, 'AbortError');
|
|
382
|
+
}
|
|
383
|
+
// Queue the LLM call to ensure serialized execution
|
|
384
|
+
return llmQueue.add(agent.id, world.id, chatId, async (queueAbortSignal) => {
|
|
385
|
+
const { signal: mergedAbortSignal, dispose } = createCombinedAbortSignal(queueAbortSignal, abortSignal);
|
|
386
|
+
try {
|
|
387
|
+
if (mergedAbortSignal?.aborted) {
|
|
388
|
+
throw new DOMException(`LLM call aborted before execution for agent ${agent.id}`, 'AbortError');
|
|
389
|
+
}
|
|
390
|
+
return await executeStreamAgentResponse(world, agent, messages, publishSSE, mergedAbortSignal);
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
dispose();
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Internal streaming implementation (executed within queue)
|
|
399
|
+
*/
|
|
400
|
+
async function executeStreamAgentResponse(world, agent, messages, publishSSE, abortSignal) {
|
|
401
|
+
const messageId = generateId();
|
|
402
|
+
try {
|
|
403
|
+
if (abortSignal?.aborted) {
|
|
404
|
+
throw new DOMException('LLM call aborted before start', 'AbortError');
|
|
405
|
+
}
|
|
406
|
+
// Publish SSE start event via world's eventEmitter
|
|
407
|
+
publishSSE(world, {
|
|
408
|
+
agentName: agent.id,
|
|
409
|
+
type: 'start',
|
|
410
|
+
messageId
|
|
411
|
+
});
|
|
412
|
+
loggerStreaming.debug(`LLM: Starting streaming response for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
413
|
+
// Convert messages for LLM (strip custom fields)
|
|
414
|
+
// Note: Client-side filtering already done by utils.ts prepareMessagesForLLM
|
|
415
|
+
let preparedMessages = stripCustomFieldsFromMessages(messages);
|
|
416
|
+
// Get MCP tools for this world
|
|
417
|
+
const mcpTools = await getMCPToolsForWorld(world.id);
|
|
418
|
+
const hasMCPTools = Object.keys(mcpTools).length > 0;
|
|
419
|
+
// Add tool usage instructions to system message when tools are available
|
|
420
|
+
preparedMessages = appendToolRulesToSystemMessage(preparedMessages, hasMCPTools);
|
|
421
|
+
if (hasMCPTools) {
|
|
422
|
+
loggerMCP.debug(`LLM: Including ${Object.keys(mcpTools).length} MCP tools for agent=${agent.id}, world=${world.id}`);
|
|
423
|
+
// Debug: Log complete tool definitions being sent to LLM
|
|
424
|
+
for (const [toolKey, toolDef] of Object.entries(mcpTools)) {
|
|
425
|
+
loggerMCP.debug(`LLM: Tool definition for ${toolKey}`, {
|
|
426
|
+
toolName: toolKey,
|
|
427
|
+
description: toolDef.description,
|
|
428
|
+
parameters: JSON.stringify(toolDef.parameters, null, 2),
|
|
429
|
+
hasExecuteFunction: typeof toolDef.execute === 'function'
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Use direct OpenAI integration for OpenAI providers
|
|
434
|
+
if (isOpenAIProvider(agent.provider)) {
|
|
435
|
+
const client = createOpenAIClientForAgent(agent);
|
|
436
|
+
const response = await streamOpenAIResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (content) => publishSSE(world, { agentName: agent.id, type: 'chunk', content, messageId }), messageId, abortSignal);
|
|
437
|
+
// Emit end event after streaming completes
|
|
438
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
439
|
+
return { response, messageId };
|
|
440
|
+
}
|
|
441
|
+
// Use direct Anthropic integration for Anthropic provider
|
|
442
|
+
if (isAnthropicProvider(agent.provider)) {
|
|
443
|
+
const client = createAnthropicClientForAgent(agent);
|
|
444
|
+
const response = await streamAnthropicResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (content) => publishSSE(world, { agentName: agent.id, type: 'chunk', content, messageId }), messageId, abortSignal);
|
|
445
|
+
// Emit end event after streaming completes
|
|
446
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
447
|
+
return { response, messageId };
|
|
448
|
+
}
|
|
449
|
+
// Use direct Google integration for Google provider
|
|
450
|
+
if (isGoogleProvider(agent.provider)) {
|
|
451
|
+
const client = createGoogleClientForAgent(agent);
|
|
452
|
+
const response = await streamGoogleResponse(client, agent.model, preparedMessages, agent, mcpTools, world, (content) => publishSSE(world, { agentName: agent.id, type: 'chunk', content, messageId }), messageId, abortSignal);
|
|
453
|
+
// Emit end event after streaming completes
|
|
454
|
+
publishSSE(world, { agentName: agent.id, type: 'end', messageId });
|
|
455
|
+
return { response, messageId };
|
|
456
|
+
}
|
|
457
|
+
// All providers now use direct integrations - no AI SDK needed
|
|
458
|
+
throw new Error(`Unsupported provider: ${agent.provider}. All providers should use direct integrations.`);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
if (isAbortError(error) || abortSignal?.aborted) {
|
|
462
|
+
publishSSE(world, {
|
|
463
|
+
agentName: agent.id,
|
|
464
|
+
type: 'end',
|
|
465
|
+
messageId
|
|
466
|
+
});
|
|
467
|
+
loggerStreaming.info(`LLM: Streaming response canceled for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
468
|
+
throw new Error(`LLM call canceled for agent ${agent.id}`);
|
|
469
|
+
}
|
|
470
|
+
// Publish SSE error event via world's eventEmitter
|
|
471
|
+
publishSSE(world, {
|
|
472
|
+
agentName: agent.id,
|
|
473
|
+
type: 'error',
|
|
474
|
+
error: error.message,
|
|
475
|
+
messageId
|
|
476
|
+
});
|
|
477
|
+
loggerStreaming.error(`LLM: Error during streaming response for agent=${agent.id}, world=${world.id}, messageId=${messageId}, error=${error.message}`);
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Non-streaming LLM call (queued)
|
|
483
|
+
*/
|
|
484
|
+
export async function generateAgentResponse(world, agent, messages, _publishSSE, skipTools, chatId = null, abortSignal) {
|
|
485
|
+
if (abortSignal?.aborted) {
|
|
486
|
+
throw new DOMException(`LLM call aborted before queue for agent ${agent.id}`, 'AbortError');
|
|
487
|
+
}
|
|
488
|
+
// Queue the LLM call to ensure serialized execution
|
|
489
|
+
return llmQueue.add(agent.id, world.id, chatId, async (queueAbortSignal) => {
|
|
490
|
+
const { signal: mergedAbortSignal, dispose } = createCombinedAbortSignal(queueAbortSignal, abortSignal);
|
|
491
|
+
try {
|
|
492
|
+
if (mergedAbortSignal?.aborted) {
|
|
493
|
+
throw new DOMException(`LLM call aborted before execution for agent ${agent.id}`, 'AbortError');
|
|
494
|
+
}
|
|
495
|
+
return await executeGenerateAgentResponse(world, agent, messages, skipTools, mergedAbortSignal);
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
dispose();
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Internal generation implementation (executed within queue)
|
|
504
|
+
*/
|
|
505
|
+
async function executeGenerateAgentResponse(world, agent, messages, skipTools, abortSignal) {
|
|
506
|
+
if (abortSignal?.aborted) {
|
|
507
|
+
throw new DOMException('LLM call aborted before start', 'AbortError');
|
|
508
|
+
}
|
|
509
|
+
const messageId = generateId();
|
|
510
|
+
// Convert messages for LLM (strip custom fields)
|
|
511
|
+
// Note: Client-side filtering already done by utils.ts prepareMessagesForLLM
|
|
512
|
+
let preparedMessages = stripCustomFieldsFromMessages(messages);
|
|
513
|
+
// Get MCP tools for this world (skip if requested, e.g., for title generation)
|
|
514
|
+
const mcpTools = skipTools ? {} : await getMCPToolsForWorld(world.id);
|
|
515
|
+
const hasMCPTools = Object.keys(mcpTools).length > 0;
|
|
516
|
+
// Add tool usage instructions to system message when tools are available
|
|
517
|
+
preparedMessages = appendToolRulesToSystemMessage(preparedMessages, hasMCPTools);
|
|
518
|
+
if (hasMCPTools) {
|
|
519
|
+
loggerMCP.debug(`LLM: Including ${Object.keys(mcpTools).length} MCP tools for agent=${agent.id}, world=${world.id}`);
|
|
520
|
+
// Debug: Log complete tool definitions being sent to LLM
|
|
521
|
+
for (const [toolKey, toolDef] of Object.entries(mcpTools)) {
|
|
522
|
+
loggerMCP.debug(`LLM: Tool definition for ${toolKey}`, {
|
|
523
|
+
toolName: toolKey,
|
|
524
|
+
description: toolDef.description,
|
|
525
|
+
parameters: JSON.stringify(toolDef.parameters, null, 2),
|
|
526
|
+
hasExecuteFunction: typeof toolDef.execute === 'function'
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
loggerGeneration.debug(`LLM: Starting non-streaming response for agent=${agent.id}, world=${world.id}`, {
|
|
531
|
+
messageCount: preparedMessages.length,
|
|
532
|
+
allMessages: preparedMessages.map(m => ({
|
|
533
|
+
role: m.role,
|
|
534
|
+
hasContent: !!m.content,
|
|
535
|
+
contentPreview: m.content?.substring(0, 50),
|
|
536
|
+
hasToolCalls: !!m.tool_calls,
|
|
537
|
+
toolCallId: m.tool_call_id,
|
|
538
|
+
messageId: m.messageId,
|
|
539
|
+
agentId: m.agentId
|
|
540
|
+
}))
|
|
541
|
+
});
|
|
542
|
+
try {
|
|
543
|
+
// Use direct OpenAI integration for OpenAI providers
|
|
544
|
+
if (isOpenAIProvider(agent.provider)) {
|
|
545
|
+
const client = createOpenAIClientForAgent(agent);
|
|
546
|
+
const response = await generateOpenAIResponse(client, agent.model, preparedMessages, agent, mcpTools, world, abortSignal);
|
|
547
|
+
// Update agent activity and LLM call count
|
|
548
|
+
agent.lastActive = new Date();
|
|
549
|
+
agent.llmCallCount++;
|
|
550
|
+
agent.lastLLMCall = new Date();
|
|
551
|
+
loggerGeneration.debug(`LLM: Finished non-streaming OpenAI response for agent=${agent.id}, world=${world.id}`, {
|
|
552
|
+
responseType: response.type,
|
|
553
|
+
contentLength: response.content?.length || 0,
|
|
554
|
+
hasToolCalls: response.type === 'tool_calls',
|
|
555
|
+
toolCallCount: response.tool_calls?.length || 0,
|
|
556
|
+
messageId
|
|
557
|
+
});
|
|
558
|
+
return { response, messageId };
|
|
559
|
+
}
|
|
560
|
+
// Use direct Anthropic integration for Anthropic provider
|
|
561
|
+
if (isAnthropicProvider(agent.provider)) {
|
|
562
|
+
const client = createAnthropicClientForAgent(agent);
|
|
563
|
+
const response = await generateAnthropicResponse(client, agent.model, preparedMessages, agent, mcpTools, world, abortSignal);
|
|
564
|
+
// Update agent activity and LLM call count
|
|
565
|
+
agent.lastActive = new Date();
|
|
566
|
+
agent.llmCallCount++;
|
|
567
|
+
agent.lastLLMCall = new Date();
|
|
568
|
+
loggerGeneration.debug(`LLM: Finished non-streaming Anthropic response for agent=${agent.id}, world=${world.id}`, {
|
|
569
|
+
responseType: response.type,
|
|
570
|
+
contentLength: response.content?.length || 0,
|
|
571
|
+
hasToolCalls: response.type === 'tool_calls',
|
|
572
|
+
toolCallCount: response.tool_calls?.length || 0,
|
|
573
|
+
messageId
|
|
574
|
+
});
|
|
575
|
+
return { response, messageId };
|
|
576
|
+
}
|
|
577
|
+
// Use direct Google integration for Google provider
|
|
578
|
+
if (isGoogleProvider(agent.provider)) {
|
|
579
|
+
const client = createGoogleClientForAgent(agent);
|
|
580
|
+
const response = await generateGoogleResponse(client, agent.model, preparedMessages, agent, mcpTools, world, abortSignal);
|
|
581
|
+
// Update agent activity and LLM call count
|
|
582
|
+
agent.lastActive = new Date();
|
|
583
|
+
agent.llmCallCount++;
|
|
584
|
+
agent.lastLLMCall = new Date();
|
|
585
|
+
loggerGeneration.debug(`LLM: Finished non-streaming Google response for agent=${agent.id}, world=${world.id}`, {
|
|
586
|
+
responseType: response.type,
|
|
587
|
+
contentLength: response.content?.length || 0,
|
|
588
|
+
hasToolCalls: response.type === 'tool_calls',
|
|
589
|
+
toolCallCount: response.tool_calls?.length || 0,
|
|
590
|
+
messageId
|
|
591
|
+
});
|
|
592
|
+
return { response, messageId };
|
|
593
|
+
}
|
|
594
|
+
// All providers now use direct integrations - no AI SDK needed
|
|
595
|
+
throw new Error(`Provider ${agent.provider} should use direct integration, not AI SDK`);
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
if (isAbortError(error) || abortSignal?.aborted) {
|
|
599
|
+
loggerGeneration.info(`LLM: Non-streaming response canceled for agent=${agent.id}, world=${world.id}, messageId=${messageId}`);
|
|
600
|
+
throw new Error(`LLM call canceled for agent ${agent.id}`);
|
|
601
|
+
}
|
|
602
|
+
loggerGeneration.error(`LLM: Error during non-streaming response for agent=${agent.id}, world=${world.id}, error=${error.message}`);
|
|
603
|
+
throw error;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Get current LLM queue status for monitoring and debugging
|
|
608
|
+
*/
|
|
609
|
+
export function getLLMQueueStatus() {
|
|
610
|
+
return llmQueue.getQueueStatus();
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Emergency function to clear the LLM queue (for debugging/admin use)
|
|
614
|
+
* Returns the number of items that were cleared
|
|
615
|
+
*/
|
|
616
|
+
export function clearLLMQueue() {
|
|
617
|
+
return llmQueue.clearQueue();
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Cancel active and pending LLM calls for a specific world/chat session.
|
|
621
|
+
*/
|
|
622
|
+
export function cancelLLMCallsForChat(worldId, chatId) {
|
|
623
|
+
return llmQueue.cancelByChat(worldId, chatId);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Check if provider uses OpenAI package (direct integration)
|
|
627
|
+
*/
|
|
628
|
+
function isOpenAIProvider(provider) {
|
|
629
|
+
return [
|
|
630
|
+
LLMProvider.OPENAI,
|
|
631
|
+
LLMProvider.AZURE,
|
|
632
|
+
LLMProvider.OPENAI_COMPATIBLE,
|
|
633
|
+
LLMProvider.XAI,
|
|
634
|
+
LLMProvider.OLLAMA // Added: Ollama now uses OpenAI-compatible endpoint
|
|
635
|
+
].includes(provider);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Check if provider uses Anthropic direct integration
|
|
639
|
+
*/
|
|
640
|
+
function isAnthropicProvider(provider) {
|
|
641
|
+
return provider === LLMProvider.ANTHROPIC;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Check if provider uses Google direct integration
|
|
645
|
+
*/
|
|
646
|
+
function isGoogleProvider(provider) {
|
|
647
|
+
return provider === LLMProvider.GOOGLE;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Create OpenAI client for agent based on provider type
|
|
651
|
+
*/
|
|
652
|
+
function createOpenAIClientForAgent(agent) {
|
|
653
|
+
const config = getLLMProviderConfig(agent.provider);
|
|
654
|
+
switch (agent.provider) {
|
|
655
|
+
case LLMProvider.OPENAI:
|
|
656
|
+
return createClientForProvider('openai', config);
|
|
657
|
+
case LLMProvider.AZURE:
|
|
658
|
+
return createClientForProvider('azure', config);
|
|
659
|
+
case LLMProvider.OPENAI_COMPATIBLE:
|
|
660
|
+
return createClientForProvider('openai-compatible', config);
|
|
661
|
+
case LLMProvider.XAI:
|
|
662
|
+
return createClientForProvider('xai', config);
|
|
663
|
+
case LLMProvider.OLLAMA:
|
|
664
|
+
return createClientForProvider('ollama', config);
|
|
665
|
+
default:
|
|
666
|
+
throw new Error(`Unsupported OpenAI provider: ${agent.provider}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
//# sourceMappingURL=llm-manager.js.map
|