feat(ideas): add idea creator, grant calls and applications
Implement the idea creator and application generator: idea status workflow, social canvas card, grant call windows with 409 when closed, and an LLM-backed idea assistant with offline fallback.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
using TarninaHub.Application.Abstractions;
|
||||
using TarninaHub.Application.Dtos;
|
||||
using TarninaHub.Application.Matchmaking;
|
||||
|
||||
namespace TarninaHub.Infrastructure.Assist;
|
||||
|
||||
/// <summary>
|
||||
/// Idea-creator assistant. Uses the configured LLM when available and falls
|
||||
/// back to deterministic, stage-aware suggestions otherwise.
|
||||
/// </summary>
|
||||
public sealed class IdeaAssistant : IIdeaAssistant
|
||||
{
|
||||
private const string SystemPrompt =
|
||||
"Jesteś asystentem kreatora innowacji społecznych w Małopolskim Hubie Innowacji Społecznych. " +
|
||||
"Rozwijasz pomysł mieszkańca: podajesz konkretne, praktyczne podpowiedzi prostym językiem. " +
|
||||
"Nie wymyślaj faktów ani danych liczbowych.";
|
||||
|
||||
private readonly IIdeaService _ideaService;
|
||||
private readonly ILlmProvider _llmProvider;
|
||||
private readonly ILlmRequestLimiter _llmRequestLimiter;
|
||||
|
||||
public IdeaAssistant(
|
||||
IIdeaService ideaService,
|
||||
ILlmProvider llmProvider,
|
||||
ILlmRequestLimiter llmRequestLimiter)
|
||||
{
|
||||
_ideaService = ideaService;
|
||||
_llmProvider = llmProvider;
|
||||
_llmRequestLimiter = llmRequestLimiter;
|
||||
}
|
||||
|
||||
public async Task<IdeaAssistResponse?> AssistAsync(
|
||||
string ideaId,
|
||||
IdeaAssistRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var idea = await _ideaService.GetAsync(ideaId, cancellationToken);
|
||||
|
||||
if (idea is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var improvedDescription = idea.Description;
|
||||
var fromLlm = false;
|
||||
|
||||
if (_llmProvider.IsConfigured && _llmRequestLimiter.TryAcquire())
|
||||
{
|
||||
var userPrompt =
|
||||
$"Pomysł: {idea.Title}\n" +
|
||||
$"Opis: {idea.Description}\n" +
|
||||
$"Etap: {idea.Card?.Stage ?? "idea"}\n" +
|
||||
$"Odbiorca: {idea.Card?.Audience ?? "nieokreślony"}\n" +
|
||||
$"Fokus: {request.Focus ?? "ogólny"}\n" +
|
||||
"Rozwiń opis pomysłu i dodaj praktyczne wskazówki.";
|
||||
|
||||
var completion = await _llmProvider.CompleteAsync(SystemPrompt, userPrompt, cancellationToken);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(completion))
|
||||
{
|
||||
improvedDescription = completion.Trim();
|
||||
fromLlm = true;
|
||||
}
|
||||
}
|
||||
|
||||
return new IdeaAssistResponse(BuildSuggestions(idea), improvedDescription, fromLlm);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> BuildSuggestions(IdeaResponse idea)
|
||||
{
|
||||
var stage = idea.Card?.Stage ?? "idea";
|
||||
var suggestions = new List<string>
|
||||
{
|
||||
"Doprecyzuj grupę docelową i jej konkretną potrzebę.",
|
||||
"Określ, jak zmierzysz efekt (wskaźniki, liczba odbiorców).",
|
||||
"Zaplanuj mały pilotaż i wskaż partnerów (NGO/JST/firma).",
|
||||
$"Dopasuj opis do etapu '{stage}' i sprawdź wymagania naboru.",
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(idea.Card?.Audience))
|
||||
{
|
||||
suggestions.Add($"Rozwiń opis odbiorcy: {idea.Card.Audience}.");
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user