- NameSuggestionService: Gemini via existing OpenAiChatClient, builds
letter/gender/usages/count prompt, strips markdown fences from response,
deserialises [{name,meaning,origin}] array; NotConfigured -> 503.
- GET /names/suggest?letter=&gender=&usages=&count= -> Ok<List<NameSuggestion>>
| 503 {code:NamesKeyMissing} | 502 {code:NamesUpstreamError}.
- Litter.LitterLetter (string?, nullable) + AddLitterLetter migration.
- 17 new tests (prompt assembly, fence strip, parse edge cases,
503-not-configured, upstream-error); total 157/157 green.
- No Behind-the-Name dependency — Gemini path only (Julian's decision).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
39 lines
1.4 KiB
C#
39 lines
1.4 KiB
C#
using GerbilManagerWebAPI.Names;
|
|
|
|
namespace GerbilManagerWebAPI.Endpoints
|
|
{
|
|
/// <summary>
|
|
/// FEAT-NAMEGEN: GET /names/suggest — meaningful gerbil name suggestions via Gemini.
|
|
///
|
|
/// 503 {code:"NamesKeyMissing"} while the AI section is unconfigured — the
|
|
/// frontend maps exactly this code to its German disabled-state hint.
|
|
/// </summary>
|
|
public static class NamesEndpoints
|
|
{
|
|
public static IEndpointRouteBuilder MapNamesEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
app.MapGet("/names/suggest", async (
|
|
string? letter,
|
|
string? gender,
|
|
string? usages,
|
|
int count,
|
|
NameSuggestionService service,
|
|
CancellationToken ct) =>
|
|
{
|
|
var result = await service.SuggestAsync(letter, gender, usages, count, ct);
|
|
return result.Status switch
|
|
{
|
|
NameSuggestionStatus.Ok => Results.Ok(result.Suggestions),
|
|
NameSuggestionStatus.NotConfigured => Results.Json(
|
|
new { code = "NamesKeyMissing", message = result.Error ?? "" }, statusCode: 503),
|
|
_ => Results.Json(
|
|
new { code = "NamesUpstreamError", message = result.Error ?? "" }, statusCode: 502),
|
|
};
|
|
})
|
|
.WithTags("Names");
|
|
|
|
return app;
|
|
}
|
|
}
|
|
}
|