From ebbe23c639eb939aa7b6e5fcb5fa9ff8972ca77a Mon Sep 17 00:00:00 2001 From: Assistant builder Date: Thu, 11 Jun 2026 21:21:13 +0000 Subject: [PATCH] Add EndSessionTool (no params, returns ok; relay closes on this tool name) --- backend.tests/EndSessionToolTests.cs | 35 ++++++++++++++++++++++++++++ backend/Tools/EndSessionTool.cs | 20 ++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 backend.tests/EndSessionToolTests.cs create mode 100644 backend/Tools/EndSessionTool.cs diff --git a/backend.tests/EndSessionToolTests.cs b/backend.tests/EndSessionToolTests.cs new file mode 100644 index 0000000..531537e --- /dev/null +++ b/backend.tests/EndSessionToolTests.cs @@ -0,0 +1,35 @@ +using System.Text.Json; +using backend.Tools; +using FluentAssertions; +using Xunit; + +namespace backend.tests; + +public class EndSessionToolTests +{ + [Fact] + public async Task Returns_ok_true() + { + var tool = new EndSessionTool(); + var ctx = new DeviceContext(Guid.NewGuid(), Guid.NewGuid(), new NoopChannel()); + + var res = await tool.ExecuteAsync( + new ToolInvocation("call_1", JsonDocument.Parse("{}").RootElement), ctx, default); + + res.Output.GetProperty("ok").GetBoolean().Should().BeTrue(); + } + + [Fact] + public void Name_and_runs_during_response_match_spec() + { + var tool = new EndSessionTool(); + tool.Name.Should().Be("end_session"); + tool.RunsDuringResponse.Should().BeFalse(); + } + + private class NoopChannel : IDeviceChannel + { + public Task CallPiToolAsync(string n, JsonElement a, CancellationToken ct) + => throw new NotSupportedException(); + } +} diff --git a/backend/Tools/EndSessionTool.cs b/backend/Tools/EndSessionTool.cs new file mode 100644 index 0000000..39d4f7f --- /dev/null +++ b/backend/Tools/EndSessionTool.cs @@ -0,0 +1,20 @@ +using System.Text.Json; + +namespace backend.Tools; + +public class EndSessionTool : ITool +{ + public string Name => "end_session"; + public string Description => + "Call this when the user has said goodbye or otherwise indicates the conversation is over."; + public bool RunsDuringResponse => false; + + public JsonElement ParameterSchema { get; } = + JsonDocument.Parse("""{"type":"object","properties":{},"required":[]}""").RootElement; + + public Task ExecuteAsync(ToolInvocation invocation, DeviceContext device, CancellationToken ct) + { + var output = JsonDocument.Parse("""{"ok":true}""").RootElement; + return Task.FromResult(new ToolResult(output)); + } +}