diff --git a/.vscode/launch.json b/.vscode/launch.json index dbc00b5b..9a2f0a3e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,6 @@ { "version": "0.2.0", - "configurations": [ + "configurations": [ { "type": "node", "request": "launch", diff --git a/README.md b/README.md index b1b655f1..f65682c3 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ English / [Japanese](./README_jp.md) > The "Generator" setting will be moved to the Avatar Settings section, and the existing "Generator" will be renamed to "Model." > We plan to clarify the relationship between Avatar AI, which is centered around context and is composed of a collection of multiple generators. +> As a temporary measure, we have added cutoffChatLimit to the LLM settings. +> This will reduce token consumption by forcibly truncating the context. + > Supported LM Studio API (OpenAI compatibility mode). > Note: If you are using a previous version and are experiencing unstable operation, please initialize the settings by going to SystemSetting > initialize. diff --git a/README_jp.md b/README_jp.md index 7fa17c8f..df99646f 100644 --- a/README_jp.md +++ b/README_jp.md @@ -10,6 +10,9 @@ Japanese / [English](./README.md) > 「ジェネレーター」の設定はアバター設定側に移動し、既存の「ジェネレーター」は「モデル」に変更します。 > アバターAIがコンテキストを中心として複数のジェネレーターの集合体で構成される関係性をより明確化する予定です。 +> 暫定的な対策としてLLM設定に cutoffChatLimit を追加しました。 +> 強制的にコンテキストを切り詰めることでトークンの消費量を抑制します。 + > LM Studio API (OpenAI互換モード)をサポートしました。 > Note: 以前の版を使っている方で、動作が不安定になった方はSystemSetting > initialize で一旦設定の初期化をお願いします。 diff --git a/package.json b/package.json index efc153b7..99a7400e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "avatar-shell", "description": "Avatar/Visually-based MCP client", - "version": "0.2.5", + "version": "0.2.6", "license": "Apache-2.0", "private": true, "type": "module", @@ -84,5 +84,8 @@ "sound-play": "^1.1.0", "ws": "^8.18.2", "zod": "^3.25.34" + }, + "volta": { + "node": "24.13.0" } } diff --git a/packages/common/DefGenerators.ts b/packages/common/DefGenerators.ts index ff45779b..34ce010a 100644 --- a/packages/common/DefGenerators.ts +++ b/packages/common/DefGenerators.ts @@ -105,6 +105,7 @@ export const ContextGeneratorSettingSchema = Schema.partial(Schema.Struct({ toContext:Schema.optional(AsContextLinesSchema), noTool: Schema.optional(Schema.Boolean), useModel: Schema.optional(Schema.String), + cutoffChatLimit: Schema.optional(Schema.Number), debug: Schema.Any, // デバッグ用汎用 })) @@ -174,7 +175,8 @@ Generator 全体設定(api keyなど全体で設定するもの const generatorCommonConfigSchema = Schema.Struct({ maxTokenThreshold:Schema.Number, - summarizePrompt:Schema.String + summarizePrompt:Schema.String, + cutoffChatLimit:Schema.optional(Schema.Number) }) // openAi openAiText, openAiImage, openAiVoice diff --git a/packages/main/package.json b/packages/main/package.json index e77bb747..74867191 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -27,5 +27,8 @@ "electron-devtools-installer": "4.0.0", "typescript": "5.8.3", "vite": "7.0.5" + }, + "volta": { + "node": "24.13.0" } } diff --git a/packages/main/src/AvatarState.ts b/packages/main/src/AvatarState.ts index a23d7a64..6ed51631 100644 --- a/packages/main/src/AvatarState.ts +++ b/packages/main/src/AvatarState.ts @@ -855,7 +855,7 @@ export class AvatarState { }); } - MaxGen = 2; // TODO 世代の最適値は + MaxGen = 20; // TODO 世代の最適値は enterInner(inner: GenInner, addToBuffer = true) { const it = this; @@ -893,6 +893,7 @@ export class AvatarState { // } yield* it.resetPrevBuffer(); it.clearStreamingText(); + // gen.setContextDelimiter() ここは次に回すgenが未確定だからデリミダ付けられないか。。 return; // func の無限ループを防ぐ } // Generator処理 @@ -922,6 +923,7 @@ export class AvatarState { yield* Queue.offerAll(it.outerQueue, io); } else { it.clearStreamingText(); + gen.setContextDelimiter() } }), step: b => loop, @@ -1057,11 +1059,12 @@ export class AvatarState { toGenerator: gen, input: message, genNum: 1, // この入力はまだcontextに追加されていないので1から開始して追加させる - setting: { - toClass: setting.toClass, - toRole: setting.toRole, - toContext: setting.toContext, - }, + setting: setting, + // setting: { + // toClass: setting.toClass, + // toRole: setting.toRole, + // toContext: setting.toContext, + // }, }, addToBuffer); // console.log('start loop'); diff --git a/packages/main/src/generators/ClaudeGenerator.ts b/packages/main/src/generators/ClaudeGenerator.ts index 769b2b7a..a52d8001 100644 --- a/packages/main/src/generators/ClaudeGenerator.ts +++ b/packages/main/src/generators/ClaudeGenerator.ts @@ -37,9 +37,9 @@ export abstract class ClaudeBaseGenerator extends ContextGenerator { protected abstract genName: GeneratorProvider; protected maxModelContextSize = 200000; // TODO Claudeの最大コンテキスト長も20Kぐらいらしい - protected get previousContexts() { - return this.previousNativeContexts as Anthropic.Messages.MessageParam[]; - } + // protected get previousContexts() { + // return this.previousNativeContexts as Anthropic.Messages.MessageParam[]; + // } static generatorInfo: ContextGeneratorInfo = { usePreviousContext: true, @@ -58,6 +58,10 @@ export abstract class ClaudeBaseGenerator extends ContextGenerator { }); } + getPreviousNativeContexts():(Anthropic.Messages.MessageParam | null)[] { + return this.previousNativeContexts as (Anthropic.Messages.MessageParam | null)[]; + } + // filterToolRes(value: ContentBlock ) { // // sysConfigでexperimental.mcpUiFilterDisabledがtrueの場合フィルタしない // if(this.sysSetting.experimental.mcpUiFilterDisabled) return [value]; @@ -279,7 +283,32 @@ export class ClaudeTextGenerator extends ClaudeBaseGenerator { return Effect.gen(function* () { // TODO prev contextを抽出(AsMessage履歴から合成またはコンテキストキャッシュから再生) const prevMake = yield* it.makePreviousContext(avatarState,current); - const prev = Array.from(it.previousContexts) // ユーザからのmcpExternalで + let prev: Anthropic.Messages.MessageParam[] = [] + const lessCutoff = it.sysSetting.generators.anthropic.common?.cutoffChatLimit || Number.MAX_SAFE_INTEGER; + if (lessCutoff !== Number.MAX_SAFE_INTEGER) { + const sum = Array.from(it.getPreviousNativeContexts()).reduce((previousValue, currentValue) => { + if(currentValue === null){ + previousValue.list.push(previousValue.buf) + return {list:previousValue.list,buf:[]} + } + return {list:previousValue.list,buf:previousValue.buf.concat(currentValue)} + },{list:[] as Anthropic.Messages.MessageParam[][],buf:[] as Anthropic.Messages.MessageParam[]}) + if(sum.buf.length > 0) { + sum.list.push(sum.buf) + } + + const cutoff = sum.list.reverse().reduce((previousValue, currentValue) => { + if (previousValue.count <= 0) { + return previousValue; + } + const next = previousValue.out.concat(currentValue.reverse()); + previousValue.count-= currentValue.length + return {out:next,count:previousValue.count} + },{out:[] as Anthropic.Messages.MessageParam[][],count:lessCutoff}) + prev = cutoff.out.reverse().flat() + } else { + prev = Array.from(it.getPreviousNativeContexts()).filter((value):value is Anthropic.Messages.MessageParam => value !== null); // ユーザからのmcpExternalで + } // 入力current GenInnerからcurrent contextを調整(input textまはたMCP responses) const mes = yield* it.makeCurrentContext(current); @@ -317,11 +346,11 @@ export class ClaudeTextGenerator extends ClaudeBaseGenerator { }, }); - it.previousContexts.push({ + it.getPreviousNativeContexts().push({ role: mes.role, content:mes.content, }) - it.previousContexts.push({ + it.getPreviousNativeContexts().push({ role: message.role, content:message.content, } as Anthropic.Messages.MessageParam) diff --git a/packages/main/src/generators/ContextGenerator.ts b/packages/main/src/generators/ContextGenerator.ts index 80477dec..22ca091d 100644 --- a/packages/main/src/generators/ContextGenerator.ts +++ b/packages/main/src/generators/ContextGenerator.ts @@ -74,6 +74,14 @@ export abstract class ContextGenerator { avatarState.clearStreamingText() } + /** + * prevContextBufferに分離可能なデリミダを追加する + * 今のところnullを使う + */ + setContextDelimiter() { + this.previousNativeContexts.push(null); + } + filterForLlmPrevContext(asMes:AsMessage[],current?:AsMessage) { // TODO ここは妥当な条件を再検討が必要。。 // currentを含まないこと、currentよりもtickが古いこと diff --git a/packages/main/src/generators/GeminiGenerator.ts b/packages/main/src/generators/GeminiGenerator.ts index 3528740d..49316a28 100644 --- a/packages/main/src/generators/GeminiGenerator.ts +++ b/packages/main/src/generators/GeminiGenerator.ts @@ -55,7 +55,7 @@ export abstract class GeminiBaseGenerator extends ContextGenerator { } protected get previousContexts() { - return this.previousNativeContexts as Content[]; + return this.previousNativeContexts as (Content | null)[]; } constructor(sysConfig: SysConfig, settings?: GeminiSettings) { @@ -155,7 +155,7 @@ export abstract class GeminiBaseGenerator extends ContextGenerator { // geminiの場合、画像ファイルはinput_imageとしてbase64で送る const media = yield* DocService.readDocMedia(current.input.content.mediaUrl); const b1 = yield* it.shrinkImage(Buffer.from(media, 'base64').buffer, it.geminiSettings?.inWidth); - const blob = new Blob([b1], {type: 'image/png'}); + const blob = new Blob([new Uint8Array(b1).buffer], {type: 'image/png'}); const myfile = yield* Effect.tryPromise(() => it.ai.files.upload({ file: blob, config: {mimeType: 'image/png'}, @@ -211,7 +211,33 @@ export class GeminiTextGenerator extends GeminiBaseGenerator { return Effect.gen(function* () { // prev contextを抽出(AsMessage履歴から合成またはコンテキストキャッシュから再生) const prevMake = yield* it.makePreviousContext(avatarState,current); - const prev = Array.from(it.previousContexts) + let prev:Content[] = [] + const lessCutoff = it.sysSetting.generators.gemini.common?.cutoffChatLimit || Number.MAX_SAFE_INTEGER; + if (lessCutoff !== Number.MAX_SAFE_INTEGER) { + const sum = Array.from(it.previousContexts).reduce((previousValue, currentValue) => { + if(currentValue === null){ + previousValue.list.push(previousValue.buf) + return {list:previousValue.list,buf:[]} + } + return {list:previousValue.list,buf:previousValue.buf.concat(currentValue)} + },{list:[] as Content[][],buf:[] as Content[]}) + if(sum.buf.length > 0) { + sum.list.push(sum.buf) + } + + const cutoff = sum.list.reverse().reduce((previousValue, currentValue) => { + if (previousValue.count <= 0) { + return previousValue; + } + const next = previousValue.out.concat(currentValue.reverse()); + previousValue.count-= currentValue.length + return {out:next,count:previousValue.count} + },{out:[] as Content[][],count:lessCutoff}) + prev = cutoff.out.reverse().flat() + } else { + prev = Array.from(it.previousContexts).filter((value):value is Content => value !== null); // ユーザからのmcpExternalで + } + // const prev = Array.from(it.previousContexts) // 入力current GenInnerからcurrent contextを調整(input textまはたMCP responses) const mes = yield* it.makeCurrentContext(current); diff --git a/packages/main/src/generators/LmStudioGenerator.ts b/packages/main/src/generators/LmStudioGenerator.ts index 7ef020db..47789ecc 100644 --- a/packages/main/src/generators/LmStudioGenerator.ts +++ b/packages/main/src/generators/LmStudioGenerator.ts @@ -47,6 +47,10 @@ export abstract class LmStudioBaseGenerator extends ContextGenerator { protected override maxModelContextSize = -1; protected baseUrl='' + getPreviousNativeContexts():(ResponseInputItem | null)[] { + return this.previousNativeContexts as (ResponseInputItem | null)[]; + } + static generatorInfo: ContextGeneratorInfo = { usePreviousContext: true, defaultPrevContextSize: 100, @@ -263,7 +267,35 @@ export class LmStudioTextGenerator extends LmStudioBaseGenerator { // prev contextを抽出(AsMessage履歴から合成またはコンテキストキャッシュから再生) const prevMake = yield* it.makePreviousContext(avatarState, current); - const prev = Array.from(it.previousNativeContexts).filter(value => !(value.type === 'message' && value.role === 'developer')) + let prev:ResponseInputItem[] = [] + const lessCutoff = it.sysSetting.generators.lmStudio.common?.cutoffChatLimit || Number.MAX_SAFE_INTEGER; + // const lessCutoff = Math.min(current.setting?.cutoffChatLimit || Number.MAX_SAFE_INTEGER, it.lmStudioSettings?.cutoffChatLimit || Number.MAX_SAFE_INTEGER); + if (lessCutoff !== Number.MAX_SAFE_INTEGER) { + const sum = Array.from(it.getPreviousNativeContexts()).reduce((previousValue, currentValue) => { + if(currentValue === null){ + previousValue.list.push(previousValue.buf) + return {list:previousValue.list,buf:[]} + } + return {list:previousValue.list,buf:previousValue.buf.concat(currentValue)} + },{list:[] as ResponseInputItem[][],buf:[] as ResponseInputItem[]}) + if(sum.buf.length > 0) { + sum.list.push(sum.buf) + } + + const cutoff = sum.list.reverse().reduce((previousValue, currentValue) => { + if (previousValue.count <= 0) { + return previousValue; + } + const next = previousValue.out.concat(currentValue.reverse()); + previousValue.count-= currentValue.length + return {out:next,count:previousValue.count} + },{out:[] as ResponseInputItem[][],count:lessCutoff}) + prev = cutoff.out.reverse().flat().filter(value => !(value.type === 'message' && value.role === 'developer')) + } else { + prev = Array.from(it.getPreviousNativeContexts()).filter((value):value is ResponseInputItem => value !== null && !(value.type === 'message' && value.role === 'developer')) + // prev = Array.from(it.getPreviousNativeContexts()).filter((value):value is Anthropic.Messages.MessageParam => value !== null); // ユーザからのmcpExternalで + } + // const prev = Array.from(it.getPreviousNativeContexts()).filter((value):value is ResponseInputItem => value !== null && !(value.type === 'message' && value.role === 'developer')) // TODO prevMakeとprevの差分チェックは後々必要 // 入力current GenInnerからcurrent contextを調整(input textまはたMCP responses) const mes = yield* it.makeCurrentContext(current); @@ -290,7 +322,7 @@ export class LmStudioTextGenerator extends LmStudioBaseGenerator { // store:true, }; mes.forEach(a => { - it.previousNativeContexts.push(a) + it.getPreviousNativeContexts().push(a) }) const res = yield* Effect.tryPromise({ @@ -328,7 +360,7 @@ export class LmStudioTextGenerator extends LmStudioBaseGenerator { ).map(a => a.response.output).flat(); console.log('responseOut:', JSON.stringify(responseOut)); responseOut.forEach(a => { - it.previousNativeContexts.push(a) // TODO ResponseOutputItemをResponseInputItemに変換する必要があるケースがあったはず。。 + it.getPreviousNativeContexts().push(a) // TODO ResponseOutputItemをResponseInputItemに変換する必要があるケースがあったはず。。 }) const textOut = responseOut.filter(b => b.type === 'message').map((b: ResponseOutputMessage) => { // TODO mesIdは1件のはず? diff --git a/packages/main/src/generators/OllamaGenerator.ts b/packages/main/src/generators/OllamaGenerator.ts index 02f11c83..7fcef0a8 100644 --- a/packages/main/src/generators/OllamaGenerator.ts +++ b/packages/main/src/generators/OllamaGenerator.ts @@ -25,10 +25,11 @@ export class OllamaTextGenerator extends ContextGenerator { protected model = 'llama3.2'; private ollama: Ollama; protected systemPrompt?:Message[]; + protected ollamaSettings: ContextGeneratorSetting | undefined; // TODO ollamaの場合最大コンテキストサイズの取得は? protected get previousContexts() { - return this.previousNativeContexts as Message[]; + return this.previousNativeContexts as (Message | null)[]; } static make(sysConfig:SysConfig, settings?: ContextGeneratorSetting) { @@ -37,6 +38,7 @@ export class OllamaTextGenerator extends ContextGenerator { constructor(setting: SysConfig,settings?: ContextGeneratorSetting) { super(setting); + this.ollamaSettings = settings; this.model = settings?.useModel || setting.generators.ollama?.model || 'llama3.1'; this.ollama = new Ollama({ host: setting.generators.ollama?.host || 'http://localhost:11434', @@ -64,7 +66,33 @@ export class OllamaTextGenerator extends ContextGenerator { images:undefined, // TODO 画像は送るべきか? } as Message] }) - const prev = Array.from(it.previousContexts) + let prev:Message[] = [] + const lessCutoff = it.sysSetting.generators.ollama.common?.cutoffChatLimit || Number.MAX_SAFE_INTEGER; + if (lessCutoff !== Number.MAX_SAFE_INTEGER) { + const sum = Array.from(it.previousContexts).reduce((previousValue, currentValue) => { + if(currentValue === null){ + previousValue.list.push(previousValue.buf) + return {list:previousValue.list,buf:[]} + } + return {list:previousValue.list,buf:previousValue.buf.concat(currentValue)} + },{list:[] as Message[][],buf:[] as Message[]}) + if(sum.buf.length > 0) { + sum.list.push(sum.buf) + } + const cutoff = sum.list.reverse().reduce((previousValue, currentValue) => { + if (previousValue.count <= 0) { + return previousValue; + } + const next = previousValue.out.concat(currentValue.reverse()); + previousValue.count-= currentValue.length + return {out:next,count:previousValue.count} + },{out:[] as Message[][],count:lessCutoff}) + prev = cutoff.out.reverse().flat() + } else { + prev = Array.from(it.previousContexts).filter((value):value is Message => value !== null); // ユーザからのmcpExternalで + } + + // const prev = Array.from(it.previousContexts) // 入力current GenInnerからcurrent contextを調整(input textまはたMCP responses) const mes:Message = { role: 'user', content: '' }; if (current.input?.content.text) { diff --git a/packages/main/src/generators/OpenAiGenerator.ts b/packages/main/src/generators/OpenAiGenerator.ts index 52560793..b72ef65d 100644 --- a/packages/main/src/generators/OpenAiGenerator.ts +++ b/packages/main/src/generators/OpenAiGenerator.ts @@ -67,8 +67,9 @@ export abstract class OpenAiBaseGenerator extends ContextGenerator { setSystemPrompt(context: string) { } + protected get previousContexts() { - return this.previousNativeContexts as OpenAI.Responses.ResponseInputItem[]; + return this.previousNativeContexts as (ResponseInputItem | null)[]; } // filterToolRes(value: any) { @@ -277,7 +278,34 @@ export class OpenAiTextGenerator extends OpenAiBaseGenerator { return Effect.gen(function* () { // prev contextを抽出(AsMessage履歴から合成またはコンテキストキャッシュから再生) const prevMake = yield* it.makePreviousContext(avatarState, current); - const prev = Array.from(it.previousContexts).filter(value => !(value.type === 'message' && value.role === 'developer')) // 古いsys promptは除去 + let prev:ResponseInputItem[] = [] + const lessCutoff = it.sysSetting.generators.openAiText.common?.cutoffChatLimit || Number.MAX_SAFE_INTEGER; + if (lessCutoff !== Number.MAX_SAFE_INTEGER) { + const sum = Array.from(it.previousContexts).reduce((previousValue, currentValue) => { + if(currentValue === null){ + previousValue.list.push(previousValue.buf) + return {list:previousValue.list,buf:[]} + } + return {list:previousValue.list,buf:previousValue.buf.concat(currentValue)} + },{list:[] as ResponseInputItem[][],buf:[] as ResponseInputItem[]}) + if(sum.buf.length > 0) { + sum.list.push(sum.buf) + } + + const cutoff = sum.list.reverse().reduce((previousValue, currentValue) => { + if (previousValue.count <= 0) { + return previousValue; + } + const next = previousValue.out.concat(currentValue.reverse()); + previousValue.count-= currentValue.length + return {out:next,count:previousValue.count} + },{out:[] as ResponseInputItem[][],count:lessCutoff}) + prev = cutoff.out.reverse().flat().filter(value => !(value.type === 'message' && value.role === 'developer')) + } else { + prev = Array.from(it.previousContexts).filter((value):value is ResponseInputItem => value !== null && !(value.type === 'message' && value.role === 'developer')) + // prev = Array.from(it.getPreviousNativeContexts()).filter((value):value is Anthropic.Messages.MessageParam => value !== null); // ユーザからのmcpExternalで + } + // const prev = Array.from(it.previousContexts).filter(value => !(value.type === 'message' && value.role === 'developer')) // 古いsys promptは除去 // TODO prevMakeとprevの差分チェックは後々必要 // 入力current GenInnerからcurrent contextを調整(input textまはたMCP responses) const mes = yield* it.makeCurrentContext(current); diff --git a/packages/main/tests/AvatarState.unit.spec.ts b/packages/main/tests/AvatarState.unit.spec.ts index 0b79945a..3c16e67d 100644 --- a/packages/main/tests/AvatarState.unit.spec.ts +++ b/packages/main/tests/AvatarState.unit.spec.ts @@ -1,16 +1,16 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; import {NodeFileSystem} from '@effect/platform-node'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {AvatarState} from '../src/AvatarState'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {AvatarState} from '../src/AvatarState.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; import {describe, expect, it} from '@effect/vitest'; -import {AsMessage, AsOutput, AvatarSettingMutable} from '../../common/Def'; +import {AsMessage, AsOutput, AvatarSettingMutable} from '../../common/Def.js'; import dayjs from 'dayjs'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {AvatarService, AvatarServiceLive} from '../src/AvatarService.js'; import {FetchHttpClient} from '@effect/platform'; const AppLive = Layer.mergeAll(MediaServiceLive, DocServiceLive, McpServiceLive, ConfigServiceLive, diff --git a/packages/main/tests/ClaudeGenerator.unit.spec.ts b/packages/main/tests/ClaudeGenerator.unit.spec.ts index a613c392..a6300ace 100644 --- a/packages/main/tests/ClaudeGenerator.unit.spec.ts +++ b/packages/main/tests/ClaudeGenerator.unit.spec.ts @@ -2,25 +2,25 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; import {runPromise} from 'effect/Effect'; import {it, expect, describe, beforeEach} from '@effect/vitest'; -import {AvatarState} from '../src/AvatarState'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {AvatarState} from '../src/AvatarState.js'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; import {FetchHttpClient, FileSystem} from '@effect/platform'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; -import {ClaudeTextGenerator} from '../src/generators/ClaudeGenerator'; -import {AsMessage} from '../../common/Def'; +import {AvatarService, AvatarServiceLive} from '../src/AvatarService.js'; +import {ClaudeTextGenerator} from '../src/generators/ClaudeGenerator.js'; +import {AsMessage} from '../../common/Def.js'; import { contextStepTest1, contextStepTest2, contextStepTest3, contextStepTest4, contextStepTest5, contextStepTest6, contextStepTest7, -} from './CommonGeneratorTest'; +} from './CommonGeneratorTest.js'; import * as path from 'node:path'; const cwd = process.cwd(); @@ -139,7 +139,7 @@ describe('ClaudeGenerator', () => { yield* Effect.sleep('1 seconds'); const pickOuter = (yield* avatarState.ScheduleList).find(d => d.name === 'pickOuter'); - const gen = yield* avatarState.getDefGenerator(pickOuter.genId); + const gen = pickOuter ? yield* avatarState.getDefGenerator(pickOuter.genId):undefined; return {avatarState, gen}; }); } diff --git a/packages/main/tests/CommonGeneratorTest.ts b/packages/main/tests/CommonGeneratorTest.ts index 365deaa3..ff85a1a6 100644 --- a/packages/main/tests/CommonGeneratorTest.ts +++ b/packages/main/tests/CommonGeneratorTest.ts @@ -1,15 +1,15 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {vitestAvatarConfigNone, vitestSysConfig} from '../../common/vitestConfig'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; -import {AsMessage} from '../../common/Def'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {vitestAvatarConfigNone, vitestSysConfig} from '../../common/vitestConfig.js'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {AvatarService, AvatarServiceLive} from '../src/AvatarService.js'; +import {AsMessage} from '../../common/Def.js'; import {expect} from '@effect/vitest'; -import {MediaServiceLive} from '../src/MediaService'; -import {DocServiceLive} from '../src/DocService'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {DocServiceLive} from '../src/DocService.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; -import {GeneratorProvider} from '../../common/DefGenerators'; +import {GeneratorProvider} from '../../common/DefGenerators.js'; import {FetchHttpClient} from '@effect/platform'; const AppLive = Layer.mergeAll(MediaServiceLive, DocServiceLive, McpServiceLive, ConfigServiceLive, @@ -30,7 +30,7 @@ function setupNormalTalkTest(vitestConf: any) { yield* Effect.sleep('1 seconds'); const pickOuter = (yield* avatarState.ScheduleList).find(d => d.name === 'pickOuter'); - const gen = yield* avatarState.getDefGenerator(pickOuter.genId); + const gen = yield* avatarState.getDefGenerator(pickOuter?.genId || ''); return {avatarState, gen}; }); } @@ -638,7 +638,7 @@ export async function contextStepTest7(generatorName:GeneratorProvider,expectedS input: { move:'D3' } - },normalTalk.genId) + },normalTalk?.genId ||'') console.log('callMcpToolByExternal:',res2); yield* Effect.sleep('60 seconds'); diff --git a/packages/main/tests/ConfigService.unit.spec.ts b/packages/main/tests/ConfigService.unit.spec.ts index 0a53445a..6f35b3dd 100644 --- a/packages/main/tests/ConfigService.unit.spec.ts +++ b/packages/main/tests/ConfigService.unit.spec.ts @@ -3,11 +3,11 @@ import {it, expect, describe} from '@effect/vitest'; import {Effect, Schema} from 'effect'; import {runPromise} from 'effect/Effect'; import {NodeFileSystem} from '@effect/platform-node'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {vitestAvatarConfigMi, vitestMutableSetting, vitestSysConfig} from '../../common/vitestConfig'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {vitestAvatarConfigMi, vitestMutableSetting, vitestSysConfig} from '../../common/vitestConfig.js'; import {FileSystem} from '@effect/platform'; -import {AvatarMcpSetting} from '../../common/Def'; -import {AvatarService} from '../src/AvatarService'; +import {AvatarMcpSetting} from '../../common/Def.js'; +import {AvatarService} from '../src/AvatarService.js'; import path from 'node:path'; const inGitHubAction = process.env.GITHUB_ACTIONS === 'true'; diff --git a/packages/main/tests/DocService.unit.spec.ts b/packages/main/tests/DocService.unit.spec.ts index 79d88ca3..b35da16b 100644 --- a/packages/main/tests/DocService.unit.spec.ts +++ b/packages/main/tests/DocService.unit.spec.ts @@ -1,16 +1,16 @@ import {it, expect, describe} from '@effect/vitest'; import {Effect, Layer} from 'effect'; import {runPromise} from 'effect/Effect'; -import {DocService, DocServiceLive} from '../src/DocService'; +import {DocService, DocServiceLive} from '../src/DocService.js'; import {NodeFileSystem} from '@effect/platform-node'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {AsOutput} from '../../common/Def'; -import {AvatarState} from '../src/AvatarState'; -import {McpServiceLive} from '../src/McpService'; -import {MediaServiceLive} from '../src/MediaService'; -import {vitestAvatarConfigMi} from '../../common/vitestConfig'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; -import {AvatarServiceLive} from '../src/AvatarService'; +import {ConfigServiceLive} from '../src/ConfigService.js'; +import {AsOutput} from '../../common/Def.js'; +import {AvatarState} from '../src/AvatarState.js'; +import {McpServiceLive} from '../src/McpService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {vitestAvatarConfigMi} from '../../common/vitestConfig.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; +import {AvatarServiceLive} from '../src/AvatarService.js'; import {FetchHttpClient} from '@effect/platform'; const inGitHubAction = process.env.GITHUB_ACTIONS === 'true'; diff --git a/packages/main/tests/GeminiGenerator.unit.spec.ts b/packages/main/tests/GeminiGenerator.unit.spec.ts index 1ec53612..38f95d3d 100644 --- a/packages/main/tests/GeminiGenerator.unit.spec.ts +++ b/packages/main/tests/GeminiGenerator.unit.spec.ts @@ -2,26 +2,26 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; import {runPromise} from 'effect/Effect'; import {it, expect, describe, beforeEach} from '@effect/vitest'; -import {AvatarState} from '../src/AvatarState'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {McpServiceLive} from '../src/McpService'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {AvatarState} from '../src/AvatarState.js'; +import {ConfigServiceLive} from '../src/ConfigService.js'; +import {McpServiceLive} from '../src/McpService.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; import {FetchHttpClient, FileSystem} from '@effect/platform'; import path from 'node:path'; -import {GeminiTextGenerator} from '../src/generators/GeminiGenerator'; -import {AvatarServiceLive} from '../src/AvatarService'; -import {AsMessage} from '../../common/Def'; +import {GeminiTextGenerator} from '../src/generators/GeminiGenerator.js'; +import {AvatarServiceLive} from '../src/AvatarService.js'; +import {AsMessage} from '../../common/Def.js'; import { contextStepTest1, contextStepTest2, contextStepTest3, contextStepTest4, contextStepTest5, contextStepTest6, contextStepTest7, -} from './CommonGeneratorTest'; +} from './CommonGeneratorTest.js'; const cwd = process.cwd(); let baseDir = cwd; diff --git a/packages/main/tests/GeneratorService.unit.spec.ts b/packages/main/tests/GeneratorService.unit.spec.ts deleted file mode 100644 index 0cda1f22..00000000 --- a/packages/main/tests/GeneratorService.unit.spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* -import {describe, expect, it} from '@effect/vitest'; -import {vitestAvatarConfigMi, vitestSysConfig} from '../../common/vitestConfig'; -import {Effect, Layer, ManagedRuntime} from 'effect'; -import {MediaServiceLive} from '../src/MediaService'; -import {DocServiceLive} from '../src/DocService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; -import {NodeFileSystem} from '@effect/platform-node'; -import {GeneratorService, GeneratorServiceLive, GenInner} from '../src/generators/GeneratorService'; -import {GeneratorProvider} from '../../common/DefGenerators'; -import {AsMessage, AsMessageContent} from '../../common/Def'; -import {z} from 'zod/index'; -import {AvatarState} from '../src/AvatarState'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; -import {FetchHttpClient} from '@effect/platform'; - - -const inGitHubAction = process.env.GITHUB_ACTIONS === 'true'; - -const AppLive = Layer.mergeAll(MediaServiceLive, DocServiceLive, McpServiceLive, ConfigServiceLive, - BuildInMcpServiceLive,AvatarServiceLive, NodeFileSystem.layer,FetchHttpClient.layer) -const aiRuntime = ManagedRuntime.make(AppLive); - -describe("GeneratorService", () => { - const testTemplateId = vitestAvatarConfigMi.templateId; - const testFileName = 'test_file_20240713000000.asdata'; - - - it('execGeneratorLoop_text', async () => { - const r = await Effect.gen(function* () { - yield* McpService.reset(vitestSysConfig); - yield *Effect.sleep('1 seconds'); - - yield *AvatarService.addAvatarQueue({templateId: 'vitestNoneId', name: 'Mix'}) - const avatarState = yield *AvatarService.makeAvatar(null) - // yield *GeneratorService.startLoop() - - // const avatarState = yield* AvatarState.make('aaaa', 'vitestNoneId', 'Mix', null, 'user'); - // const avatarState = yield* AvatarState.make('aaaa', 'vitestDummyId', 'Mix', null, 'user'); - yield *Effect.sleep('5 seconds'); - - const res = yield *avatarState.enterInner({ - avatarId:avatarState.Id, - toGenerator:'ollamaText', - input:{ - from: 'user', - text: 'hello' - }, - genNum:0, - setting: { - noTool:true - } - }) - console.log('enterInner:',res); - - yield *Effect.sleep('30 seconds'); - - const context = yield *avatarState.TalkContextEffect; - console.log('context:',context) - return context - }).pipe( - aiRuntime.runPromise, - ) - console.log('r:',r); - }) - it('addContext', async () => { - // vitest --run --testNamePattern=make AvatarState.unit.spec.ts - const res = await Effect.gen(function* () { - yield* McpService.reset(vitestSysConfig); - - const avatarState = yield* AvatarState.make('aaaa', 'vitestNoneId', 'Mix', null, 'user'); - // console.log(avatarState); - yield* Effect.sleep('5 seconds'); // avatarState生成直後はスケジュールリストはまだ更新されていない - yield* avatarState.addContext([ - AsMessage.makeMessage({ - from: 'human', - text: 'hello', - },'talk','human','surface') - ]); - return yield* avatarState.TalkContextEffect; - }).pipe( - aiRuntime.runPromise, - ); - - console.log(JSON.stringify(res, null, 2)); - expect(typeof res === 'object').toBeTruthy(); - expect(res.length).toBe(1); - }); - - it('execGeneratorLoop_text2', async () => { - await Effect.gen(function* () { - yield* McpService.reset(vitestSysConfig); - yield *Effect.sleep('1 seconds'); - - yield *AvatarService.addAvatarQueue({templateId: 'vitestNoneId', name: 'Mix'}) - const avatarState = yield *AvatarService.makeAvatar(null) - // yield *GeneratorService.startLoop() - // const avatarState = yield* AvatarState.make('aaaa', 'vitestDummyId', 'Mix', null, 'user'); - yield *Effect.sleep('1 seconds'); - - const gen = yield* avatarState.getDefGenerator('aaa'); - const res = yield *avatarState.enterInner({ - avatarId:avatarState.Id, - fromGenerator:'emptyText', - toGenerator:gen, - input:AsMessage.makeMessage({ - from: 'human', - text: 'hello', - },'talk','human','surface'), - genNum:0, - setting: { - noTool:true - } - }) - console.log('enterInner:',res); - - yield *Effect.sleep('20 seconds'); - - const res2 = yield *avatarState.enterInner({ - avatarId:avatarState.Id, - fromGenerator:'emptyText', - toGenerator:gen, - input:AsMessage.makeMessage({ - from: 'human', - text: "What should I do when it's hot?", - },'talk','human','surface'), - genNum:0, - setting: { - noTool:true - } - }) - console.log('enterInner2:',res2); - - yield *Effect.sleep('30 seconds'); - - console.log('context:',yield *avatarState.TalkContextEffect) - - }).pipe( - aiRuntime.runPromise, - ) - }) - it('execGeneratorLoop_mcp', async () => { - await Effect.gen(function* () { - yield* McpService.reset(vitestSysConfig); - yield *Effect.sleep('1 seconds'); - - yield *AvatarService.addAvatarQueue({templateId: 'vitestNoneId', name: 'Mix'}) - const avatarState = yield *AvatarService.makeAvatar(null) - // const avatarState = yield* AvatarState.make('aaaa', 'vitestDummyId', 'Mix', null, 'user'); - yield *Effect.sleep('1 seconds'); - - const res = yield *GeneratorService.enterInner({ - avatarId:avatarState.Id, - toGenerator:'ollamaText', - input:{ - from: 'user', - text: '/get traveler setting' - }, - genNum:0, - }) - console.log('enterInner:',res); - - yield *Effect.sleep('30 seconds'); - - }).pipe( - aiRuntime.runPromise, - ) - }) -},5 * 60 * 1000) -*/ diff --git a/packages/main/tests/LmStudioGenerator.unit.spec.ts b/packages/main/tests/LmStudioGenerator.unit.spec.ts index 8ef0f6f6..6c8dcfd6 100644 --- a/packages/main/tests/LmStudioGenerator.unit.spec.ts +++ b/packages/main/tests/LmStudioGenerator.unit.spec.ts @@ -2,26 +2,26 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; import {runPromise} from 'effect/Effect'; import {it, expect, describe, beforeEach} from '@effect/vitest'; -import {AvatarState} from '../src/AvatarState'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {AvatarState} from '../src/AvatarState.js'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; import {FetchHttpClient, FileSystem} from '@effect/platform'; import path from 'node:path'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; -import {AsMessage} from '../../common/Def'; +import {AvatarService, AvatarServiceLive} from '../src/AvatarService.js'; +import {AsMessage} from '../../common/Def.js'; import { contextStepTest1, contextStepTest2, contextStepTest3, contextStepTest4, contextStepTest5, contextStepTest6, contextStepTest7, -} from './CommonGeneratorTest'; -import {LmStudioTextGenerator} from '../src/generators/LmStudioGenerator'; +} from './CommonGeneratorTest.js'; +import {LmStudioTextGenerator} from '../src/generators/LmStudioGenerator.js'; const cwd = process.cwd() let baseDir = cwd; @@ -110,7 +110,7 @@ describe('LmStudioGenerator', () => { yield* Effect.sleep('1 seconds'); const pickOuter = (yield* avatarState.ScheduleList).find(d => d.name === 'pickOuter'); - const gen = yield* avatarState.getDefGenerator(pickOuter.genId); + const gen = yield* avatarState.getDefGenerator(pickOuter?.genId || ''); return {avatarState, gen}; }); } diff --git a/packages/main/tests/McpService.unit.spec.ts b/packages/main/tests/McpService.unit.spec.ts index 32cbb328..b0bfb403 100644 --- a/packages/main/tests/McpService.unit.spec.ts +++ b/packages/main/tests/McpService.unit.spec.ts @@ -3,15 +3,15 @@ import {Effect, Layer} from 'effect'; import {runPromise, runPromiseExit} from 'effect/Effect'; import {NodeFileSystem} from '@effect/platform-node'; import {it, expect, describe, beforeEach, afterEach} from '@effect/vitest'; -import {AvatarState} from '../src/AvatarState'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; -import {vitestAvatarConfigMi, vitestSysConfig} from '../../common/vitestConfig'; -import {AvatarMcpSettingListMutable, AvatarSetting} from '../../common/Def'; -import {AvatarServiceLive} from '../src/AvatarService'; +import {AvatarState} from '../src/AvatarState.js'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; +import {vitestAvatarConfigMi, vitestSysConfig} from '../../common/vitestConfig.js'; +import {AvatarMcpSettingListMutable, AvatarSetting} from '../../common/Def.js'; +import {AvatarServiceLive} from '../src/AvatarService.js'; import {FetchHttpClient} from '@effect/platform'; const AppLive = Layer.mergeAll(MediaServiceLive, DocServiceLive, McpServiceLive, ConfigServiceLive, diff --git a/packages/main/tests/MediaService.unit.spec.ts b/packages/main/tests/MediaService.unit.spec.ts index e02f13f0..c2848a35 100644 --- a/packages/main/tests/MediaService.unit.spec.ts +++ b/packages/main/tests/MediaService.unit.spec.ts @@ -3,8 +3,8 @@ import {Effect} from 'effect'; import {runPromise} from 'effect/Effect'; import {NodeFileSystem} from '@effect/platform-node'; import path from 'path'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {MediaService, MediaServiceLive} from '../src/MediaService'; +import {ConfigServiceLive} from '../src/ConfigService.js'; +import {MediaService, MediaServiceLive} from '../src/MediaService.js'; import * as fs from 'fs'; diff --git a/packages/main/tests/OllamaGenerator.unit.spec.ts b/packages/main/tests/OllamaGenerator.unit.spec.ts index cdc2aac8..a7eee3ba 100644 --- a/packages/main/tests/OllamaGenerator.unit.spec.ts +++ b/packages/main/tests/OllamaGenerator.unit.spec.ts @@ -2,26 +2,26 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; import {runPromise} from 'effect/Effect'; import {it, expect, describe, beforeEach} from '@effect/vitest'; -import {AvatarState, GenInner} from '../src/AvatarState'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {McpServiceLive} from '../src/McpService'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {OllamaTextGenerator} from '../src/generators/OllamaGenerator'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {AvatarState, GenInner} from '../src/AvatarState.js'; +import {ConfigServiceLive} from '../src/ConfigService.js'; +import {McpServiceLive} from '../src/McpService.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {OllamaTextGenerator} from '../src/generators/OllamaGenerator.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; import {FetchHttpClient, FileSystem} from '@effect/platform'; import path from 'node:path'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {AvatarServiceLive} from '../src/AvatarService'; -import {AsMessage} from '../../common/Def'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {AvatarServiceLive} from '../src/AvatarService.js'; +import {AsMessage} from '../../common/Def.js'; import { contextStepTest1, contextStepTest2, contextStepTest3, contextStepTest4, contextStepTest5, contextStepTest6, contextStepTest7, -} from './CommonGeneratorTest'; +} from './CommonGeneratorTest.js'; const cwd = process.cwd(); let baseDir = cwd; diff --git a/packages/main/tests/OpenAiGenerator.unit.spec.ts b/packages/main/tests/OpenAiGenerator.unit.spec.ts index 51e699f4..1b4ab211 100644 --- a/packages/main/tests/OpenAiGenerator.unit.spec.ts +++ b/packages/main/tests/OpenAiGenerator.unit.spec.ts @@ -2,26 +2,26 @@ import {Effect, Layer, ManagedRuntime} from 'effect'; import {runPromise} from 'effect/Effect'; import {it, expect, describe, beforeEach} from '@effect/vitest'; -import {AvatarState} from '../src/AvatarState'; -import {ConfigService, ConfigServiceLive} from '../src/ConfigService'; -import {McpService, McpServiceLive} from '../src/McpService'; -import {DocService, DocServiceLive} from '../src/DocService'; -import {MediaServiceLive} from '../src/MediaService'; -import {vitestSysConfig} from '../../common/vitestConfig'; -import {BuildInMcpServiceLive} from '../src/BuildInMcpService'; +import {AvatarState} from '../src/AvatarState.js'; +import {ConfigService, ConfigServiceLive} from '../src/ConfigService.js'; +import {McpService, McpServiceLive} from '../src/McpService.js'; +import {DocService, DocServiceLive} from '../src/DocService.js'; +import {MediaServiceLive} from '../src/MediaService.js'; +import {vitestSysConfig} from '../../common/vitestConfig.js'; +import {BuildInMcpServiceLive} from '../src/BuildInMcpService.js'; import {NodeFileSystem} from '@effect/platform-node'; import {FetchHttpClient, FileSystem} from '@effect/platform'; import path from 'node:path'; -import {AvatarService, AvatarServiceLive} from '../src/AvatarService'; -import {OpenAiTextGenerator, OpenAiImageGenerator, OpenAiVoiceGenerator} from '../src/generators/OpenAiGenerator'; -import {AsMessage} from '../../common/Def'; +import {AvatarService, AvatarServiceLive} from '../src/AvatarService.js'; +import {OpenAiTextGenerator, OpenAiImageGenerator, OpenAiVoiceGenerator} from '../src/generators/OpenAiGenerator.js'; +import {AsMessage} from '../../common/Def.js'; import { contextStepTest1, contextStepTest2, contextStepTest3, contextStepTest4, contextStepTest5, contextStepTest6, contextStepTest7, -} from './CommonGeneratorTest'; +} from './CommonGeneratorTest.js'; const cwd = process.cwd() let baseDir = cwd; @@ -124,8 +124,9 @@ describe('OpenAiGenerator', () => { if (o.outputText) { return Effect.succeed(o.outputText); } + return Effect.succeed(''); }) - }).pipe(aiRuntime.runPromise,); + }).pipe(aiRuntime.runPromise); console.log('out:',res); @@ -156,6 +157,7 @@ describe('OpenAiGenerator', () => { if (o.outputText) { return Effect.succeed(o.outputText); } + return Effect.succeed(''); }) }).pipe(aiRuntime.runPromise,); @@ -177,7 +179,7 @@ describe('OpenAiGenerator', () => { yield* Effect.sleep('1 seconds'); const pickOuter = (yield* avatarState.ScheduleList).find(d => d.name === 'pickOuter'); - const gen = yield* avatarState.getDefGenerator(pickOuter.genId); + const gen = yield* avatarState.getDefGenerator(pickOuter?.genId || ''); return {avatarState, gen}; }); } diff --git a/packages/main/tests/SocketService.unit.spec.ts b/packages/main/tests/SocketService.unit.spec.ts deleted file mode 100644 index f67d912d..00000000 --- a/packages/main/tests/SocketService.unit.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* - -import { Effect, Layer } from 'effect'; -import {expect, describe, it, afterEach, afterAll} from 'vitest'; -import { io as Client } from 'socket.io-client'; -import { SocketServiceLive } from '../src/SocketService'; -import { ConfigServiceLive } from '../src/ConfigService'; -import {AsMessage} from '../../common/Def'; -import {runPromise} from 'effect/Effect'; -import {beforeAll} from '@effect/vitest'; - -const AppConfigLive = Layer.merge(ConfigServiceLive,SocketServiceLive); // TODO なぜかメモ化は走らない? - -describe('SocketService', () => { - - let clientSocket: ReturnType; - - beforeAll(async () => { - await Effect.gen(function* () { - }).pipe( - Effect.tap(a => Effect.log(a)), - Effect.provide(AppConfigLive), - runPromise, - ); - }); - - afterEach(async () => { - if (clientSocket) { - clientSocket.close(); - } - }); - - afterAll(async suite => { - // await Effect.runPromise(SocketService.close().pipe(Effect.provide(AppConfigLive))); // TODO - }) - - it.sequential('start', async () => { - // vitest --run --testNamePattern=start SocketService.unit.spec.ts - // クライアント接続を試行 - clientSocket = Client(`http://localhost:3000`); - - await new Promise((resolve) => { - clientSocket.on('connect', () => { - resolve(); - }); - }); - - expect(clientSocket.connected).toBe(true); - }); - - it.sequential('asMessageイベントが正しくブロードキャストされること', async () => { - - const testMessage: AsMessage = { - id:'aaa', - tick:1, - asClass:'system', - asRole:'system', - asContext:'outer', - isRequestAction:false, - content:{ - text:'test data' - } - }; - - // 2つのクライアントを接続 - const client1 = Client(`http://localhost:3000`); - const client2 = Client(`http://localhost:3000`); - - await Promise.all([ - new Promise(resolve => client1.on('connect', resolve)), - new Promise(resolve => client2.on('connect', resolve)) - ]); - - const messagePromise = new Promise(resolve => { - client2.on('asMessage', (msg) => { - resolve(msg); - }); - }); - - // client1からメッセージを送信 - client1.emit('asMessage', testMessage); - - const receivedMessage = await messagePromise; - expect(receivedMessage).toEqual(testMessage); - - client1.close(); - client2.close(); - }) - -/!* -TODO - it.sequential('設定変更時にサーバーが再起動すること', async () => { - await Effect.gen(function* () { - const sysConfig = yield *ConfigService.getSysConfig() - // yield* SocketService.boot() - - // 一旦サーバーを停止 - const mockConfigRef:SysConfigMutable = { - ...sysConfig, - websocket: {...sysConfig.websocket, useServer: false} - } - yield *ConfigService.updateSysConfig(() => mockConfigRef) - }).pipe( - Effect.tap(a => Effect.log(a)), - Effect.provide(AppConfigLive), - runPromise, - ); - - // 接続を試みて失敗することを確認 - await expect( - new Promise((_, reject) => { - const socket = Client(`http://localhost:3000`, { - timeout: 1000 - }); - socket.on('connect_error', () => { - socket.close(); - reject(new Error('Connection failed as expected')); - }); - }) - ).rejects.toThrow(); - - // サーバーを再起動 - await Effect.gen(function* () { - const sysConfig = yield *ConfigService.getSysConfig() - // yield* SocketService.boot() - - // 一旦サーバーを停止 - const mockConfigRef:SysConfigMutable = { - ...sysConfig, - websocket: {...sysConfig.websocket, useServer: true} - } - yield *ConfigService.updateSysConfig(() => mockConfigRef) - }).pipe( - Effect.tap(a => Effect.log(a)), - Effect.provide(AppConfigLive), - runPromise, - ); - - // 接続が成功することを確認 - clientSocket = Client(`http://localhost:3000`); - await new Promise((resolve) => { - clientSocket.on('connect', resolve); - }); - - expect(clientSocket.connected).toBe(true); - }); -*!/ -},5*60*1000); -*/ diff --git a/packages/main/tests/main.unit.spec.ts b/packages/main/tests/main.unit.spec.ts index b023f66d..d625921e 100644 --- a/packages/main/tests/main.unit.spec.ts +++ b/packages/main/tests/main.unit.spec.ts @@ -3,8 +3,8 @@ import {test} from 'vitest'; import {Effect} from 'effect'; import path from 'node:path'; import {runPromise} from 'effect/Effect'; -import {ConfigServiceLive} from '../src/ConfigService'; -import {MediaService, MediaServiceLive} from '../src/MediaService'; +import {ConfigServiceLive} from '../src/ConfigService.js'; +import {MediaService, MediaServiceLive} from '../src/MediaService.js'; test('MediaService_playSound', async () => { diff --git a/packages/main/tsconfig.test.json b/packages/main/tsconfig.test.json new file mode 100644 index 00000000..5842bccb --- /dev/null +++ b/packages/main/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "isolatedModules": false + }, + "include": ["src/**/*.ts", "tests/**/*.ts", "../../types/**/*.d.ts"], + "exclude": [] +} diff --git a/packages/main/vitest.config.ts b/packages/main/vitest.config.ts new file mode 100644 index 00000000..a1fdd7d4 --- /dev/null +++ b/packages/main/vitest.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vitest/config'; +import * as path from 'path'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + alias: { + '@app/preload': path.resolve(__dirname, '../preload'), + '@app/renderer': path.resolve(__dirname, '../renderer'), + }, + }, + resolve: { + alias: { + '@app/preload': path.resolve(__dirname, '../preload'), + '@app/renderer': path.resolve(__dirname, '../renderer'), + }, + }, +}); diff --git a/packages/renderer/src/components/AvatarSettingPanel.vue b/packages/renderer/src/components/AvatarSettingPanel.vue index 3c4e79f8..82cd3725 100644 --- a/packages/renderer/src/components/AvatarSettingPanel.vue +++ b/packages/renderer/src/components/AvatarSettingPanel.vue @@ -344,6 +344,9 @@ onMounted(async () => { {{$t('execution')}} +
{{$t('outputContextAttr')}}
diff --git a/packages/renderer/src/components/SysSettingPanel.vue b/packages/renderer/src/components/SysSettingPanel.vue index 99e10cdd..28245ada 100644 --- a/packages/renderer/src/components/SysSettingPanel.vue +++ b/packages/renderer/src/components/SysSettingPanel.vue @@ -444,14 +444,19 @@ const copyPath = async () => { placeholder="gpt-4.1-mini" :label="t('textModel')" data-testid="openai-model" /> - + @@ -487,14 +492,19 @@ const copyPath = async () => { placeholder="claude-3-7-sonnet-latest" :label="t('textModel')" data-testid="anthropic-model" /> - + @@ -505,14 +515,19 @@ const copyPath = async () => { - + @@ -548,14 +563,19 @@ const copyPath = async () => { placeholder="llama3.1" :label="t('Model')" data-testid="ollama-model" /> - + @@ -574,14 +594,19 @@ const copyPath = async () => { placeholder="openai/gpt-oss-20b" :label="t('defaultModel')" data-testid="lmStudio-model" /> - +