Allow hook to intercept function.

This commit is contained in:
Haiping Chen 2025-04-20 18:24:13 -05:00
parent 3deb6deee5
commit a791600c40
7 changed files with 38 additions and 12 deletions

View file

@ -12,7 +12,7 @@ public interface IAgentService
Task<Agent> CreateAgent(Agent agent);
Task<string> RefreshAgents();
Task<PagedItems<Agent>> GetAgents(AgentFilter filter);
Task<List<IdName>> GetAgentOptions(List<string>? agentIds = null);
Task<List<IdName>> GetAgentOptions(List<string>? agentIds = null, bool byName = false);
/// <summary>
/// Load agent configurations and trigger hooks

View file

@ -66,6 +66,12 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? FunctionArgs { get; set; }
/// <summary>
/// Set this flag is in OnFunctionExecuting, if true, it won't be executed by InvokeFunction.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public bool Handled { get; set; } = false;
/// <summary>
/// Function execution structured data, this data won't pass to LLM.
/// It's ideal to render in rich content in UI.

View file

@ -63,7 +63,7 @@ public class RealtimeConversationHook : ConversationHookBase, IConversationHook
}
else
{
await hub.Completer.TriggerModelInference($"{instruction}\r\n\r\nResponse user based on function result");
await hub.Completer.TriggerModelInference(instruction);
}
}
}

View file

@ -26,12 +26,18 @@ public partial class AgentService
}
[SharpCache(10)]
public async Task<List<IdName>> GetAgentOptions(List<string>? agentIds)
public async Task<List<IdName>> GetAgentOptions(List<string>? agentIdsOrNames, bool byName = false)
{
var agents = _db.GetAgents(new AgentFilter
{
AgentIds = !agentIds.IsNullOrEmpty() ? agentIds : null
});
var agents = byName ?
_db.GetAgents(new AgentFilter
{
AgentNames = !agentIdsOrNames.IsNullOrEmpty() ? agentIdsOrNames : null
}) :
_db.GetAgents(new AgentFilter
{
AgentIds = !agentIdsOrNames.IsNullOrEmpty() ? agentIdsOrNames : null
});
return agents?.Select(x => new IdName(x.Id, x.Name))?.OrderBy(x => x.Name)?.ToList() ?? [];
}

View file

@ -86,10 +86,12 @@ public class RoutingContext : IRoutingContext
if (!Guid.TryParse(agentId, out _))
{
var agentService = _services.GetRequiredService<IAgentService>();
agentId = agentService.GetAgents(new AgentFilter
var agents = agentService.GetAgentOptions([agentId], byName: true).Result;
if (agents.Count > 0)
{
AgentNames = [agentId]
}).Result.Items.First().Id;
agentId = agents.First().Id;
}
}
if (_stack.Count == 0 || _stack.Peek() != agentId)

View file

@ -49,8 +49,11 @@ public partial class RoutingService
await progressService.OnFunctionExecuting(clonedMessage);
}
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.GetAgent(clonedMessage.CurrentAgentId);
foreach (var hook in hooks)
{
hook.SetAgent(agent);
await hook.OnFunctionExecuting(clonedMessage);
}
@ -58,7 +61,11 @@ public partial class RoutingService
try
{
if (!isFillDummyContent)
if (clonedMessage.Handled)
{
clonedMessage.Content = clonedMessage.Content;
}
else if (!isFillDummyContent)
{
result = await function.Execute(clonedMessage);
}

View file

@ -162,7 +162,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
}
else if (response.Type == "response.audio_transcript.delta")
{
_logger.LogDebug($"{response.Type}: {receivedText}");
}
else if (response.Type == "response.audio_transcript.done")
{
@ -211,9 +211,14 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
}
else if (response.Type == "input_audio_buffer.speech_started")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
// Handle user interuption
onInterruptionDetected();
}
else if (response.Type == "input_audio_buffer.speech_stopped")
{
_logger.LogInformation($"{response.Type}: {receivedText}");
}
}
}