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.
88 lines
3.0 KiB
C#
88 lines
3.0 KiB
C#
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;
|
|
}
|
|
}
|