Repository navigation
Make AgentChat and AgentServices implement IServiceProvider #671
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestfailed-verificationBug failed automated verificationBug failed automated verification
Description
Summary
Make AgentChat implement IServiceProvider so pipeline-level callers (e.g. CopilotSdkChatClient) can resolve services via GetService<T>() without needing separate methods. Also add a typed IServiceProvider implementation to AgentServices.
Part of #659.
Prerequisites
- #670 —
IRunningAgentChatFactory(must be a registered service inAgentServices.GetService)
Design
AgentChat implementation
AgentChat implements IServiceProvider delegating in order:
chatClientAgent.GetService(serviceType)— the fullIChatClientpipeline (coversICopilotSubAgentReceiver,ISubAgentTable, etc.)AgentServices.GetService(serviceType)— fallback for services onAgentServices
// AgentChat : IServiceProvider
public object? GetService(Type serviceType)
{
return this.chatClientAgent?.GetService(serviceType)
?? this.request.AgentServices?.GetService(serviceType);
}AgentServices implementation
Explicit typed switch — no reflection. Returns null for unknown types. New properties added to AgentServices must also be added here.
// AgentServices : IServiceProvider
public object? GetService(Type serviceType)
{
if (serviceType == typeof(ILoggerFactory)) return LoggerFactory;
if (serviceType == typeof(IAgentPersistenceStore)) return AgentPersistenceStoreOverride;
if (serviceType == typeof(IRunningAgentChatFactory)) return RunningAgentChatFactory;
if (serviceType == typeof(IToolsetFactory)) return ToolsetFactory;
if (serviceType == typeof(IToolResourceFactory)) return ToolResourceFactory;
return null;
}Files affected
| File | Change |
|---|---|
Phantom.Workspaces.Llm.Core/AgentChat.cs |
Implement IServiceProvider |
Phantom.Workspaces.Llm.Interfaces/AgentServices.cs |
Implement IServiceProvider with explicit typed switch |
Expected tests
| Test name | What it verifies |
|---|---|
AgentChat_GetService_ICopilotSubAgentReceiver_ReturnsCopilotSubAgentChatClient |
agentChat.GetService<ICopilotSubAgentReceiver>() returns the client from the chatClientAgent pipeline |
AgentChat_GetService_IRunningAgentChatFactory_ReturnsFromAgentServices |
agentChat.GetService<IRunningAgentChatFactory>() falls back to AgentServices and returns the factory |
AgentChat_GetService_ILoggerFactory_ReturnsFromAgentServices |
agentChat.GetService<ILoggerFactory>() falls back to AgentServices |
AgentChat_GetService_UnknownType_ReturnsNull |
agentChat.GetService(typeof(string)) returns null rather than throwing |
AgentChat_GetService_WhenChatClientReturnsNull_FallsBackToAgentServices |
If chatClientAgent.GetService returns null, AgentChat continues to query AgentServices |
AgentChat_GetService_WhenNoChatClient_FallsBackToAgentServices |
When chatClientAgent is null, AgentChat still returns services from AgentServices |
AgentServices_GetService_ReturnsNullForUnknownType |
AgentServices.GetService(typeof(object)) returns null |
AgentServices_GetService_ReturnsRegisteredService_ForEachKnownType |
One test case per registered type in the explicit switch: ILoggerFactory, IAgentPersistenceStore, IRunningAgentChatFactory, IToolsetFactory, IToolResourceFactory |
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestfailed-verificationBug failed automated verificationBug failed automated verification
Activity
JoshuaRowePhantom commentedon Jul 3, 2026
Resolution
Approach: Implement IServiceProvider on both AgentServices and AgentChat, with a typed switch (no reflection) on AgentServices and delegation to the chat client pipeline then AgentServices fallback on AgentChat.
Changes:
ull\ for unknown types
equest.AgentServices?.GetService\
Deviations from design: The service-locator key for \IRunningAgentChatFactory\ is the marker interface (\Phantom.Workspaces.Llm.Interfaces.IRunningAgentChatFactory) since \AgentServices\ lives in the Interfaces project and cannot reference the Core full interface. The Core full interface now extends the marker to allow direct assignment.
Commit: 4700803
JoshuaRowePhantom commentedon Jul 6, 2026
Verification failed
Checked: Commit 4700803 on eatures branch — Phantom.Workspaces.Llm.Core/AgentChat.cs, Phantom.Workspaces.Llm.Interfaces/AgentServices.cs, Phantom.Workspaces.Llm.Core.Tests/AgentChatServiceProviderTests.cs (5 tests), Phantom.Workspaces.Llm.Core.Tests/AgentServicesTests.cs (2 tests).
Found: AgentChat correctly implements IServiceProvider delegating to chatClientAgent?.GetService then AgentServices?.GetService. AgentServices correctly implements IServiceProvider with a typed switch over all 5 known types. AgentServices_GetService_ReturnsNullForUnknownType, AgentServices_GetService_ReturnsRegisteredService_ForEachKnownType, AgentChat_GetService_IRunningAgentChatFactory_ReturnsFromAgentServices, AgentChat_GetService_ILoggerFactory_ReturnsFromAgentServices, AgentChat_GetService_UnknownType_ReturnsNull, AgentChat_GetService_WhenNoChatClient_FallsBackToAgentServices — all present.
Missing: Two of the eight tests explicitly listed in the issue's Expected tests table are absent:
ull. The existing WhenNoChatClient test covers chatClientAgent == null but not the distinct case where the pipeline returns
ull.
Fix #671: make AgentChat and AgentServices implement IServiceProvider