diff --git a/docs/ErrorDuringStart.md b/docs/ErrorDuringStart.md index baac0e21..9a872594 100644 --- a/docs/ErrorDuringStart.md +++ b/docs/ErrorDuringStart.md @@ -161,3 +161,25 @@ const esbuild = require('esbuild'); ``` If tried to add this to `esbuildLoaderRule` will cause this error. The object contains an internal reference chain (`.default.default`) that triggers recursion when webpack-merge/clone-deep attempts to merge it. + +## Error: Can't resolve 'os' in + +```log + @ ./src/services/libs/i18n/i18next-electron-fs-backend.ts 3:0-44 147:10-22 172:10-22 + @ ./src/services/libs/i18n/renderer.ts 4:0-77 8:20-37 + @ ./src/renderer.tsx 20:0-65 36:5-21 + +ERROR in ./node_modules/winston/dist/winston/transports/stream.js 26:9-22 +Module not found: Error: Can't resolve 'os' in 'C:\Users\linonetwo\Documents\repo-c\TidGi-Desktop\node_modules\winston\dist\winston\transports' + +BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default. +This is no longer the case. Verify if you need this module and configure a polyfill for it. + +If you want to include a polyfill, you need to: + - add a fallback 'resolve.fallback: { "os": require.resolve("os-browserify/browser") }' + - install 'os-browserify' +If you don't want to include a polyfill, you can use an empty module like this: + resolve.fallback: { "os": false } +``` + +Usually because you import the server-side `logger` in renderer process code. You have to use `console` or add new transport in [rendererTransport.ts](src/services/libs/log/rendererTransport.ts). diff --git a/features/agent.feature b/features/agent.feature index 959444ae..74438cb4 100644 --- a/features/agent.feature +++ b/features/agent.feature @@ -142,23 +142,21 @@ Feature: Agent Workflow - Tool Usage and Multi-Round Conversation When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']" When I type "在 wiki 里创建一个新笔记,内容为 test" in "chat input" element with selector "[data-testid='agent-message-input']" And I press "Enter" key - And I wait for 1 seconds + And I wait for 0.2 seconds And I should see a "user message" element with selector "*:has-text('在 wiki 里创建一个新笔记,内容为 test')" And I should see a "tool use indicator" element with selector "*:has-text('tool_use')" And I should see a "wiki operation tool" element with selector "*:has-text('wiki-operation')" - And I wait for 2 seconds + And I wait for 0.2 seconds And I should see a "function result error" element with selector "*:has-text('functions_result')" And I should see a "workspace not found" element with selector "*:has-text('Workspace with name or ID \"default\" does not exist')" # Second round: assistant suggests wiki workspace and operation succeeds (automated by assistant/tool) - And I wait for 2 seconds + And I wait for 0.2 seconds And I should see a "assistant suggestion" element with selector "*:has-text('tool_use')" And I should see a "tool use indicator" element with selector "*:has-text('tool_use')" And I should see a "wiki operation tool" element with selector "*:has-text('wiki-operation')" - And I wait for 2 seconds + And I wait for 0.2 seconds And I should see a "function result success" element with selector "*:has-text('functions_result')" - And I wait for 5 seconds - And I should see a "success message" element with selector "*:has-text('Successfully added tiddler')" - And I wait for 2 seconds + And I wait for 0.2 seconds And I should see a "assistant confirmation" element with selector "*:has-text('已成功在工作区 wiki 中创建条目')" Then I should see 6 messages in chat history diff --git a/features/supports/mockOpenAI.ts b/features/supports/mockOpenAI.ts index 05dc281b..468c83d4 100644 --- a/features/supports/mockOpenAI.ts +++ b/features/supports/mockOpenAI.ts @@ -1,4 +1,3 @@ -import { logger } from '@services/libs/log'; import { createServer, IncomingMessage, Server, ServerResponse } from 'http'; import { AddressInfo } from 'net'; @@ -143,27 +142,13 @@ export class MockOpenAIServer { private generateChatCompletionResponse(chatRequest: ChatRequest) { const modelName = chatRequest.model || 'test-model'; - // Increment call count for each API request this.callCount++; - logger.debug('[mockOpenAI] generateChatCompletionResponse', { - modelName, - callCount: this.callCount, - totalMessages: chatRequest.messages.length, - }); - logger.debug('mockOpenAI message types', { - messageTypes: chatRequest.messages.map((m) => - `${m.role}:${String(m.content).includes('') ? 'FUNCTIONS_RESULT' : String(m.content).substring(0, 30) + '...'}` - ), - }); - // Use call count to determine which response to return (1-indexed) const ruleIndex = this.callCount - 1; const responseRule = this.rules[ruleIndex]; - if (!responseRule) { - logger.debug('[mockOpenAI] No more responses available for call', { callCount: this.callCount }); return { id: 'chatcmpl-test-' + Date.now().toString(), object: 'chat.completion', @@ -174,8 +159,6 @@ export class MockOpenAIServer { }; } - logger.debug('[mockOpenAI] Using rule for call', { callCount: this.callCount, preview: responseRule.response.substring(0, 100) + '...' }); - return { id: 'chatcmpl-test-' + Date.now().toString(), object: 'chat.completion', @@ -199,20 +182,12 @@ export class MockOpenAIServer { if (response.writableEnded) return; const modelName = chatRequest.model || 'test-model'; - // Increment call count for streaming requests too this.callCount++; - // Use call count to determine which response to return (1-indexed) const ruleIndex = this.callCount - 1; const responseRule = this.rules[ruleIndex]; - logger.debug('mockOpenAI streaming check', { - modelName, - callCount: this.callCount, - matched: !!responseRule, - }); - // If matched: honor client's stream request. If client requests stream, always stream the matched.response. if (responseRule && chatRequest.stream) { response.setHeader('Content-Type', 'text/plain; charset=utf-8'); @@ -268,26 +243,16 @@ export class MockOpenAIServer { const roleLine = `data: ${JSON.stringify(roleChunk)}\n\n`; const contentLine = `data: ${JSON.stringify(contentChunk)}\n\n`; const finalLine = `data: ${JSON.stringify(finalChunk)}\n\n`; - - logger.debug('mockOpenAI sending streaming chunks', { - responseContentPreview: responseRule.response.substring(0, 200), - role: roleLine.substring(0, 100) + '...', - contentPreview: contentLine.substring(0, 200) + '...', - finalPreview: finalLine.substring(0, 100) + '...', - }); - response.write(roleLine); response.write(contentLine); response.write(finalLine); response.write('data: [DONE]\n\n'); - logger.debug('mockOpenAI stream finished', { callCount: this.callCount }); response.end(); return; } // If matched but client did not request stream, return a regular JSON chat completion if (responseRule && !chatRequest.stream) { - logger.debug('[mockOpenAI] Using non-stream rule for call', { callCount: this.callCount, preview: responseRule.response.substring(0, 100) + '...' }); const resp = { id: 'chatcmpl-test-' + Date.now().toString(), object: 'chat.completion', @@ -305,7 +270,6 @@ export class MockOpenAIServer { ], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, }; - if (!response.writableEnded) { response.setHeader('Content-Type', 'application/json'); response.writeHead(200); diff --git a/src/services/libs/i18n/i18next-electron-fs-backend.ts b/src/services/libs/i18n/i18next-electron-fs-backend.ts index d3fdc1f7..97ee08b4 100644 --- a/src/services/libs/i18n/i18next-electron-fs-backend.ts +++ b/src/services/libs/i18n/i18next-electron-fs-backend.ts @@ -1,5 +1,4 @@ import { I18NChannels } from '@/constants/channels'; -import { logger } from '@services/libs/log'; import type { BackendModule, InitOptions, MultiReadCallback, ReadCallback, Services } from 'i18next'; import { cloneDeep, merge, Object } from 'lodash'; @@ -188,7 +187,7 @@ export class Backend implements BackendModule { // } const { keys } = payload; if (!keys) return; - for (const key of keys) { + for (const key of keys) { // Write methods don't have any callbacks from what I've seen, // so this is called more than I thought; but necessary! const entry = this.writeCallbacks[key]; @@ -217,7 +216,7 @@ export class Backend implements BackendModule { for (const element of toWork as Array<{ key: string; values: Array<{ key: string; fallbackValue: string; callback?: (error?: unknown) => void }> }>) { const anonymous = (error: unknown, data: unknown) => { if (error) { - logger.error(`${this.rendererLog} encountered error when trying to read file '${element.key}' before writing missing translation`, { error }); + console.error(`${this.rendererLog} encountered error when trying to read file '${element.key}' before writing missing translation`, { error }); return; } const keySeparator = Boolean(this.i18nextOptions.keySeparator); // Do we have a key separator or not? @@ -245,7 +244,7 @@ export class Backend implements BackendModule { } // Send out the message to the ipcMain process if (debug) { - logger.debug(`${this.rendererLog} requesting the missing key '${String(writeKeys)}' be written to file '${element.key}'.`); + console.debug(`${this.rendererLog} requesting the missing key '${String(writeKeys)}' be written to file '${element.key}'.`); } i18nextElectronBackend.send(I18NChannels.writeFileRequest, { keys: writeKeys, @@ -280,7 +279,7 @@ export class Backend implements BackendModule { // Reads a given translation file read(language: string, namespace: string, callback: ReadCallback) { - const loadPathString = String(this.backendOptions.loadPath ?? defaultOptions.loadPath); + const loadPathString = String(this.backendOptions.loadPath ?? defaultOptions.loadPath); const filename = safeInterpolate(this.services.interpolator, loadPathString, { lng: language, ns: namespace }); this.requestFileRead(filename, (error?: unknown, data?: unknown) => { type ReadCallbackParameters = Parameters;