diff --git a/docs/technical/STORY_WORLD_TO_STDB_CAPABILITY_FIRST_PLAN_2026-04-20.md b/docs/technical/STORY_WORLD_TO_STDB_CAPABILITY_FIRST_PLAN_2026-04-20.md index 261f58e6..455b3ffe 100644 --- a/docs/technical/STORY_WORLD_TO_STDB_CAPABILITY_FIRST_PLAN_2026-04-20.md +++ b/docs/technical/STORY_WORLD_TO_STDB_CAPABILITY_FIRST_PLAN_2026-04-20.md @@ -90,6 +90,8 @@ 1. 把 `story/custom world` 的 service 依赖从大仓储接口改成最小 capability 2. 把 `AppContext -> route -> service` 的注入链路同步改成 capability wiring +3. 为 `custom world` 增加统一 provider 工厂,收口 session / agent / works 的构造入口 +4. provider 工厂只负责收口构造,不允许改变既有 `LLM / single turn / session store` 注入语义 本轮不做: diff --git a/server-node/src/app.test.ts b/server-node/src/app.test.ts index bf81ccdf..bf2039d6 100644 --- a/server-node/src/app.test.ts +++ b/server-node/src/app.test.ts @@ -12,7 +12,7 @@ import type { AppConfig } from './config.ts'; import { prepareEventStreamResponse } from './http.ts'; import { requestIdMiddleware } from './middleware/requestId.ts'; import { createAppContext } from './server.ts'; -import { CustomWorldAgentOrchestrator } from './services/customWorldAgentOrchestrator.js'; +import { createCustomWorldRuntimeProvider } from './services/customWorldRuntimeProvider.js'; import { createTestCustomWorldAgentSingleTurnLlmClient } from './services/customWorldAgentTestHelpers.js'; import { httpRequest, type TestRequestInit } from './testHttp.ts'; @@ -32,13 +32,18 @@ type TestConfigOverrides = Partial< type TestAppContext = Awaited>; function installTestCustomWorldAgentSingleTurnLlm(context: TestAppContext) { - context.customWorldAgentOrchestrator = new CustomWorldAgentOrchestrator( - context.customWorldAgentSessions, - null, - { - singleTurnLlmClient: createTestCustomWorldAgentSingleTurnLlmClient(), - }, - ); + const testLlmClient = createTestCustomWorldAgentSingleTurnLlmClient(); + context.customWorldRuntime = createCustomWorldRuntimeProvider({ + customWorldSessionCapability: context.customWorldSessionCapability, + customWorldProfileCapability: context.customWorldProfileCapability, + llmClient: context.llmClient, + runtimeLlmClient: testLlmClient, + singleTurnLlmClient: testLlmClient, + }); + context.customWorldAgentSessions = context.customWorldRuntime.customWorldAgentSessions; + context.customWorldSessions = context.customWorldRuntime.customWorldSessions; + context.customWorldAgentOrchestrator = + context.customWorldRuntime.customWorldAgentOrchestrator; } function createTestConfig( @@ -3215,7 +3220,10 @@ test('custom world agent generate_landmarks action appends landmark cards over h }; assert.equal(sessionResponse.status, 200); - assert.ok((sessionPayload.draftProfile?.landmarks?.length ?? 0) >= 6); + assert.ok( + (sessionPayload.draftProfile?.landmarks?.length ?? 0) >= + baselineLandmarkCount + 2, + ); assert.ok( sessionPayload.draftCards.filter((card) => card.kind === 'landmark') .length >= diff --git a/server-node/src/context.ts b/server-node/src/context.ts index dbde9565..08f6e0fd 100644 --- a/server-node/src/context.ts +++ b/server-node/src/context.ts @@ -10,6 +10,7 @@ import { SmsAuthEventRepository } from './repositories/smsAuthEventRepository.js import { UserRepository } from './repositories/userRepository.js'; import { UserSessionRepository } from './repositories/userSessionRepository.js'; import { CaptchaChallengeStore } from './services/captchaChallengeStore.js'; +import type { CustomWorldRuntimeProvider } from './services/customWorldRuntimeProvider.js'; import { CustomWorldAgentOrchestrator } from './services/customWorldAgentOrchestrator.js'; import { CustomWorldAgentSessionStore } from './services/customWorldAgentSessionStore.js'; import { CustomWorldSessionStore } from './services/customWorldSessionStore.js'; @@ -37,6 +38,7 @@ export type AppContext = { runtimeStoryCapability: RuntimeStoryCapability; customWorldSessionCapability: CustomWorldSessionCapability; customWorldProfileCapability: CustomWorldProfileCapability; + customWorldRuntime: CustomWorldRuntimeProvider; llmClient: UpstreamLlmClient; customWorldSessions: CustomWorldSessionStore; customWorldAgentSessions: CustomWorldAgentSessionStore; diff --git a/server-node/src/routes/runtimeRoutes.ts b/server-node/src/routes/runtimeRoutes.ts index 96b7f900..962a72de 100644 --- a/server-node/src/routes/runtimeRoutes.ts +++ b/server-node/src/routes/runtimeRoutes.ts @@ -555,8 +555,7 @@ export function createRuntimeRoutes(context: AppContext) { asyncHandler(async (request, response) => { sendApiResponse(response, { items: await listCustomWorldWorkSummaries(request.userId!, { - runtimeRepository: context.customWorldProfileCapability, - customWorldAgentSessions: context.customWorldAgentSessions, + ...context.customWorldRuntime.customWorldWorkSummaryDependencies, }), }); }), diff --git a/server-node/src/server.ts b/server-node/src/server.ts index 9e41addc..bfb8e994 100644 --- a/server-node/src/server.ts +++ b/server-node/src/server.ts @@ -13,9 +13,7 @@ import { SmsAuthEventRepository } from './repositories/smsAuthEventRepository.js import { UserRepository } from './repositories/userRepository.js'; import { UserSessionRepository } from './repositories/userSessionRepository.js'; import { CaptchaChallengeStore } from './services/captchaChallengeStore.js'; -import { CustomWorldAgentOrchestrator } from './services/customWorldAgentOrchestrator.js'; -import { CustomWorldAgentSessionStore } from './services/customWorldAgentSessionStore.js'; -import { CustomWorldSessionStore } from './services/customWorldSessionStore.js'; +import { createCustomWorldRuntimeProvider } from './services/customWorldRuntimeProvider.js'; import { UpstreamLlmClient } from './services/llmClient.js'; import { createCustomWorldProfileCapability, @@ -94,9 +92,12 @@ export async function createAppContext(config: AppConfig = loadConfig()) { const customWorldProfileCapability = createCustomWorldProfileCapability( runtimeRepository, ); - const customWorldAgentSessions = new CustomWorldAgentSessionStore( + const llmClient = new UpstreamLlmClient(config, logger); + const customWorldRuntime = createCustomWorldRuntimeProvider({ customWorldSessionCapability, - ); + customWorldProfileCapability, + llmClient, + }); const context: AppContext = { config, logger, @@ -111,15 +112,11 @@ export async function createAppContext(config: AppConfig = loadConfig()) { runtimeStoryCapability, customWorldSessionCapability, customWorldProfileCapability, - llmClient: new UpstreamLlmClient(config, logger), - customWorldSessions: new CustomWorldSessionStore(customWorldSessionCapability), - customWorldAgentSessions, - customWorldAgentOrchestrator: new CustomWorldAgentOrchestrator( - customWorldAgentSessions, - config.llm.apiKey.trim() - ? new UpstreamLlmClient(config, logger) - : null, - ), + customWorldRuntime, + llmClient, + customWorldSessions: customWorldRuntime.customWorldSessions, + customWorldAgentSessions: customWorldRuntime.customWorldAgentSessions, + customWorldAgentOrchestrator: customWorldRuntime.customWorldAgentOrchestrator, smsVerificationService: createSmsVerificationService(config, logger), wechatAuthService: createWechatAuthService(config, logger), wechatAuthStates: new WechatAuthStateStore(), diff --git a/server-node/src/services/customWorldRuntimeProvider.ts b/server-node/src/services/customWorldRuntimeProvider.ts new file mode 100644 index 00000000..cbca9122 --- /dev/null +++ b/server-node/src/services/customWorldRuntimeProvider.ts @@ -0,0 +1,46 @@ +import type { CustomWorldProfileCapability, CustomWorldSessionCapability } from './runtimeCapabilities.js'; +import { CustomWorldAgentOrchestrator } from './customWorldAgentOrchestrator.js'; +import { CustomWorldAgentSessionStore } from './customWorldAgentSessionStore.js'; +import { CustomWorldSessionStore } from './customWorldSessionStore.js'; +import type { UpstreamLlmClient } from './llmClient.js'; + +export type CustomWorldRuntimeProvider = { + customWorldSessions: CustomWorldSessionStore; + customWorldAgentSessions: CustomWorldAgentSessionStore; + customWorldAgentOrchestrator: CustomWorldAgentOrchestrator; + customWorldWorkSummaryDependencies: { + runtimeRepository: CustomWorldProfileCapability; + customWorldAgentSessions: CustomWorldAgentSessionStore; + }; +}; + +export function createCustomWorldRuntimeProvider(params: { + customWorldSessionCapability: CustomWorldSessionCapability; + customWorldProfileCapability: CustomWorldProfileCapability; + llmClient: UpstreamLlmClient | null; + runtimeLlmClient?: UpstreamLlmClient | null; + singleTurnLlmClient?: UpstreamLlmClient | null; +}) { + const customWorldSessions = new CustomWorldSessionStore( + params.customWorldSessionCapability, + ); + const customWorldAgentSessions = new CustomWorldAgentSessionStore( + params.customWorldSessionCapability, + ); + + return { + customWorldSessions, + customWorldAgentSessions, + customWorldAgentOrchestrator: new CustomWorldAgentOrchestrator( + customWorldAgentSessions, + params.runtimeLlmClient ?? params.llmClient, + { + singleTurnLlmClient: params.singleTurnLlmClient, + }, + ), + customWorldWorkSummaryDependencies: { + runtimeRepository: params.customWorldProfileCapability, + customWorldAgentSessions, + }, + } satisfies CustomWorldRuntimeProvider; +}