11// Copyright (c) Microsoft. All rights reserved.
22
3+ using System . Runtime . CompilerServices ;
34using System . Text . Json ;
45
56using Microsoft . Agents . AI . DevUI . Entities ;
67using Microsoft . Agents . AI . Hosting ;
8+ using Microsoft . Agents . AI . Workflows ;
79
810namespace Microsoft . Agents . AI . DevUI ;
911
@@ -56,79 +58,19 @@ private static async Task<IResult> ListEntitiesAsync(
5658 {
5759 var entities = new List < EntityInfo > ( ) ;
5860
59- // Discover agents from the agent catalog
60- if ( agentCatalog is not null )
61+ // Discover agents
62+ await foreach ( var agentInfo in DiscoverAgentsAsync ( agentCatalog , entityIdFilter : null , cancellationToken ) . ConfigureAwait ( false ) )
6163 {
62- await foreach ( var agent in agentCatalog . GetAgentsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
63- {
64- if ( agent . GetType ( ) . Name == "WorkflowHostAgent" )
65- {
66- // HACK: ignore WorkflowHostAgent instances as they are just wrappers around workflows,
67- // and workflows are handled below.
68- continue ;
69- }
70-
71- entities . Add ( new EntityInfo (
72- Id : agent . Name ?? agent . Id ,
73- Type : "agent" ,
74- Name : agent . Name ?? agent . Id ,
75- Description : agent . Description ,
76- Framework : "agent-framework" ,
77- Tools : null ,
78- Metadata : [ ]
79- )
80- {
81- Source = "in_memory"
82- } ) ;
83- }
64+ entities . Add ( agentInfo ) ;
8465 }
8566
86- // Discover workflows from the workflow catalog
87- if ( workflowCatalog is not null )
67+ // Discover workflows
68+ await foreach ( var workflowInfo in DiscoverWorkflowsAsync ( workflowCatalog , entityIdFilter : null , cancellationToken ) . ConfigureAwait ( false ) )
8869 {
89- await foreach ( var workflow in workflowCatalog . GetWorkflowsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
90- {
91- // Extract executor IDs from the workflow structure
92- var executorIds = new HashSet < string > { workflow . StartExecutorId } ;
93- var reflectedEdges = workflow . ReflectEdges ( ) ;
94- foreach ( var ( sourceId , edgeSet ) in reflectedEdges )
95- {
96- executorIds . Add ( sourceId ) ;
97- foreach ( var edge in edgeSet )
98- {
99- foreach ( var sinkId in edge . Connection . SinkIds )
100- {
101- executorIds . Add ( sinkId ) ;
102- }
103- }
104- }
105-
106- // Create a default input schema (string type)
107- var defaultInputSchema = new Dictionary < string , object >
108- {
109- [ "type" ] = "string"
110- } ;
111-
112- entities . Add ( new EntityInfo (
113- Id : workflow . Name ?? workflow . StartExecutorId ,
114- Type : "workflow" ,
115- Name : workflow . Name ?? workflow . StartExecutorId ,
116- Description : workflow . Description ,
117- Framework : "agent-framework" ,
118- Tools : [ .. executorIds ] ,
119- Metadata : [ ]
120- )
121- {
122- Source = "in_memory" ,
123- WorkflowDump = JsonSerializer . SerializeToElement ( workflow . ToDevUIDict ( ) ) ,
124- InputSchema = JsonSerializer . SerializeToElement ( defaultInputSchema ) ,
125- InputTypeName = "string" ,
126- StartExecutorId = workflow . StartExecutorId
127- } ) ;
128- }
70+ entities . Add ( workflowInfo ) ;
12971 }
13072
131- return Results . Json ( new DiscoveryResponse ( entities ) , EntitiesJsonContext . Default . DiscoveryResponse ) ;
73+ return Results . Json ( new DiscoveryResponse ( [ .. entities ] ) , EntitiesJsonContext . Default . DiscoveryResponse ) ;
13274 }
13375 catch ( Exception ex )
13476 {
@@ -141,93 +83,26 @@ private static async Task<IResult> ListEntitiesAsync(
14183
14284 private static async Task < IResult > GetEntityInfoAsync (
14385 string entityId ,
86+ string ? type ,
14487 AgentCatalog ? agentCatalog ,
14588 WorkflowCatalog ? workflowCatalog ,
14689 CancellationToken cancellationToken )
14790 {
14891 try
14992 {
150- // Try to find the entity among discovered agents
151- if ( agentCatalog is not null )
93+ if ( type is null || string . Equals ( type , "agent" , StringComparison . OrdinalIgnoreCase ) )
15294 {
153- await foreach ( var agent in agentCatalog . GetAgentsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
95+ await foreach ( var agentInfo in DiscoverAgentsAsync ( agentCatalog , entityId , cancellationToken ) . ConfigureAwait ( false ) )
15496 {
155- if ( agent . GetType ( ) . Name == "WorkflowHostAgent" )
156- {
157- // HACK: ignore WorkflowHostAgent instances as they are just wrappers around workflows,
158- // and workflows are handled below.
159- continue ;
160- }
161-
162- if ( string . Equals ( agent . Name , entityId , StringComparison . OrdinalIgnoreCase ) ||
163- string . Equals ( agent . Id , entityId , StringComparison . OrdinalIgnoreCase ) )
164- {
165- var entityInfo = new EntityInfo (
166- Id : agent . Name ?? agent . Id ,
167- Type : "agent" ,
168- Name : agent . Name ?? agent . Id ,
169- Description : agent . Description ,
170- Framework : "agent-framework" ,
171- Tools : null ,
172- Metadata : [ ]
173- )
174- {
175- Source = "in_memory"
176- } ;
177-
178- return Results . Json ( entityInfo , EntitiesJsonContext . Default . EntityInfo ) ;
179- }
97+ return Results . Json ( agentInfo , EntitiesJsonContext . Default . EntityInfo ) ;
18098 }
18199 }
182100
183- // Try to find the entity among discovered workflows
184- if ( workflowCatalog is not null )
101+ if ( type is null || string . Equals ( type , "workflow" , StringComparison . OrdinalIgnoreCase ) )
185102 {
186- await foreach ( var workflow in workflowCatalog . GetWorkflowsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
103+ await foreach ( var workflowInfo in DiscoverWorkflowsAsync ( workflowCatalog , entityId , cancellationToken ) . ConfigureAwait ( false ) )
187104 {
188- var workflowId = workflow . Name ?? workflow . StartExecutorId ;
189- if ( string . Equals ( workflowId , entityId , StringComparison . OrdinalIgnoreCase ) )
190- {
191- // Extract executor IDs from the workflow structure
192- var executorIds = new HashSet < string > { workflow . StartExecutorId } ;
193- var reflectedEdges = workflow . ReflectEdges ( ) ;
194- foreach ( var ( sourceId , edgeSet ) in reflectedEdges )
195- {
196- executorIds . Add ( sourceId ) ;
197- foreach ( var edge in edgeSet )
198- {
199- foreach ( var sinkId in edge . Connection . SinkIds )
200- {
201- executorIds . Add ( sinkId ) ;
202- }
203- }
204- }
205-
206- // Create a default input schema (string type)
207- var defaultInputSchema = new Dictionary < string , object >
208- {
209- [ "type" ] = "string"
210- } ;
211-
212- var entityInfo = new EntityInfo (
213- Id : workflowId ,
214- Type : "workflow" ,
215- Name : workflow . Name ?? workflow . StartExecutorId ,
216- Description : workflow . Description ,
217- Framework : "agent-framework" ,
218- Tools : [ .. executorIds ] ,
219- Metadata : [ ]
220- )
221- {
222- Source = "in_memory" ,
223- WorkflowDump = JsonSerializer . SerializeToElement ( workflow . ToDevUIDict ( ) ) ,
224- InputSchema = JsonSerializer . SerializeToElement ( defaultInputSchema ) ,
225- InputTypeName = "Input" ,
226- StartExecutorId = workflow . StartExecutorId
227- } ;
228-
229- return Results . Json ( entityInfo , EntitiesJsonContext . Default . EntityInfo ) ;
230- }
105+ return Results . Json ( workflowInfo , EntitiesJsonContext . Default . EntityInfo ) ;
231106 }
232107 }
233108
@@ -241,4 +116,123 @@ private static async Task<IResult> GetEntityInfoAsync(
241116 title : "Error getting entity info" ) ;
242117 }
243118 }
119+
120+ private static async IAsyncEnumerable < EntityInfo > DiscoverAgentsAsync (
121+ AgentCatalog ? agentCatalog ,
122+ string ? entityIdFilter ,
123+ [ EnumeratorCancellation ] CancellationToken cancellationToken )
124+ {
125+ if ( agentCatalog is null )
126+ {
127+ yield break ;
128+ }
129+
130+ await foreach ( var agent in agentCatalog . GetAgentsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
131+ {
132+ // If filtering by entity ID, skip non-matching agents
133+ if ( entityIdFilter is not null &&
134+ ! string . Equals ( agent . Name , entityIdFilter , StringComparison . OrdinalIgnoreCase ) &&
135+ ! string . Equals ( agent . Id , entityIdFilter , StringComparison . OrdinalIgnoreCase ) )
136+ {
137+ continue ;
138+ }
139+
140+ yield return CreateAgentEntityInfo ( agent ) ;
141+
142+ // If we found the entity we're looking for, we're done
143+ if ( entityIdFilter is not null )
144+ {
145+ yield break ;
146+ }
147+ }
148+ }
149+
150+ private static async IAsyncEnumerable < EntityInfo > DiscoverWorkflowsAsync (
151+ WorkflowCatalog ? workflowCatalog ,
152+ string ? entityIdFilter ,
153+ [ EnumeratorCancellation ] CancellationToken cancellationToken )
154+ {
155+ if ( workflowCatalog is null )
156+ {
157+ yield break ;
158+ }
159+
160+ await foreach ( var workflow in workflowCatalog . GetWorkflowsAsync ( cancellationToken ) . ConfigureAwait ( false ) )
161+ {
162+ var workflowId = workflow . Name ?? workflow . StartExecutorId ;
163+
164+ // If filtering by entity ID, skip non-matching workflows
165+ if ( entityIdFilter is not null && ! string . Equals ( workflowId , entityIdFilter , StringComparison . OrdinalIgnoreCase ) )
166+ {
167+ continue ;
168+ }
169+
170+ yield return CreateWorkflowEntityInfo ( workflow ) ;
171+
172+ // If we found the entity we're looking for, we're done
173+ if ( entityIdFilter is not null )
174+ {
175+ yield break ;
176+ }
177+ }
178+ }
179+
180+ private static EntityInfo CreateAgentEntityInfo ( AIAgent agent )
181+ {
182+ var entityId = agent . Name ?? agent . Id ;
183+ return new EntityInfo (
184+ Id : entityId ,
185+ Type : "agent" ,
186+ Name : entityId ,
187+ Description : agent . Description ,
188+ Framework : "agent-framework" ,
189+ Tools : null ,
190+ Metadata : [ ]
191+ )
192+ {
193+ Source = "in_memory"
194+ } ;
195+ }
196+
197+ private static EntityInfo CreateWorkflowEntityInfo ( Workflow workflow )
198+ {
199+ // Extract executor IDs from the workflow structure
200+ var executorIds = new HashSet < string > { workflow . StartExecutorId } ;
201+ var reflectedEdges = workflow . ReflectEdges ( ) ;
202+ foreach ( var ( sourceId , edgeSet ) in reflectedEdges )
203+ {
204+ executorIds . Add ( sourceId ) ;
205+ foreach ( var edge in edgeSet )
206+ {
207+ foreach ( var sinkId in edge . Connection . SinkIds )
208+ {
209+ executorIds . Add ( sinkId ) ;
210+ }
211+ }
212+ }
213+
214+ // Create a default input schema (string type)
215+ var defaultInputSchema = new Dictionary < string , object >
216+ {
217+ [ "type" ] = "string"
218+ } ;
219+
220+ var workflowId = workflow . Name ?? workflow . StartExecutorId ;
221+ return new EntityInfo (
222+ Id : workflowId ,
223+ Type : "workflow" ,
224+ Name : workflowId ,
225+ Description : workflow . Description ,
226+ Framework : "agent-framework" ,
227+ Tools : [ .. executorIds ] ,
228+ Metadata : [ ]
229+ )
230+ {
231+ Source = "in_memory" ,
232+ WorkflowDump = JsonSerializer . SerializeToElement ( workflow . ToDevUIDict ( ) ) ,
233+ InputSchema = JsonSerializer . SerializeToElement ( defaultInputSchema ) ,
234+ InputTypeName = "string" ,
235+ StartExecutorId = workflow . StartExecutorId
236+ } ;
237+ }
244238}
0 commit comments