feat(router,server): wire workflow inventory and project profile into router context per turn

This commit is contained in:
2026-06-11 13:15:32 +04:00
parent 823c2e0ade
commit f1fc43cc85
4 changed files with 110 additions and 5 deletions
@@ -1274,6 +1274,77 @@ class RouterFacadeTest {
assertEquals(0, threeArgCallCount, "onUserInput must not call 3-arg route()")
}
// --------------------------------------------------------------------------
// A4 — workflowSummaryProvider and sessionProfileProvider forwarding
// --------------------------------------------------------------------------
@Test
fun `workflowSummaryProvider is called and forwarded to context builder on each turn`(): Unit = runBlocking {
val captured = mutableListOf<List<WorkflowSummary>>()
val capturingBuilder = object : RouterContextBuilder {
override suspend fun build(
state: RouterState,
budget: TokenBudget,
availableWorkflows: List<WorkflowSummary>,
projectProfileText: String?,
): ContextPack {
captured.add(availableWorkflows)
return emptyContextPack()
}
}
val workflow = WorkflowSummary("wf", "desc", listOf("s1"))
val facade = facadeWith(contextBuilder = capturingBuilder, workflowSummaryProvider = { listOf(workflow) })
facade.onUserInput(SessionId("s"), "hello")
assertEquals(listOf(listOf(workflow)), captured)
}
@Test
fun `sessionProfileProvider is called and forwarded to context builder`(): Unit = runBlocking {
val capturedProfiles = mutableListOf<String?>()
val capturingBuilder = object : RouterContextBuilder {
override suspend fun build(
state: RouterState,
budget: TokenBudget,
availableWorkflows: List<WorkflowSummary>,
projectProfileText: String?,
): ContextPack {
capturedProfiles.add(projectProfileText)
return emptyContextPack()
}
}
val facade = facadeWith(
contextBuilder = capturingBuilder,
sessionProfileProvider = { "profile text" },
)
facade.onUserInput(SessionId("s"), "hello")
assertEquals(listOf("profile text"), capturedProfiles)
}
private fun facadeWith(
contextBuilder: RouterContextBuilder = object : RouterContextBuilder {
override suspend fun build(
state: RouterState,
budget: TokenBudget,
availableWorkflows: List<WorkflowSummary>,
projectProfileText: String?,
): ContextPack = emptyContextPack()
},
workflowSummaryProvider: () -> List<WorkflowSummary> = { emptyList() },
sessionProfileProvider: suspend (SessionId) -> String? = { null },
): RouterFacade = DefaultRouterFacade(
routerRepository = object : RouterRepository {
override suspend fun getRouterState(sessionId: SessionId): RouterState = RouterState(sessionId = sessionId)
},
routerContextBuilder = contextBuilder,
inferenceRouter = mockInferenceRouter("inference response"),
eventStore = mockEventStore(),
config = RouterConfig(tokenBudget = TokenBudget(limit = 5000)),
embedder = NoopEmbedder(dimension = 8),
l3MemoryStore = InMemoryL3MemoryStore(),
workflowSummaryProvider = workflowSummaryProvider,
sessionProfileProvider = sessionProfileProvider,
)
private fun emptyContextPack(): ContextPack = ContextPack(
id = ContextPackId("empty"),
sessionId = SessionId("unknown"),