-
Notifications
You must be signed in to change notification settings - Fork 7
AI Blazor Server App pro tvorbu NFT pomocí AI
V tomto článku se dozvíte, jak vytvořit jednoduchý server, který pomáhá vytvářet dataset pro učení AI za pomoci NFT.

Aplikace, která je zde popsána, má jednoduchý úkol. Nejdříve ChatGPT vytvoří text pro uživatele na základě základní myšlenky, kterou mu uživatel zadal. Následně podle textu vytvoří název, tagy a popis. Poté další DALL-E-2 AI modul vytvoří 4 návrhy obrázků, které uživatel ohodnotí, a nakonec vymintuje NFT. Ideální je, když uživatel provede i kontrolu textu, který ChatGPT vytvoří, či jinak doplní / upraví informace v textu obsažené. Při mintování se totiž uloží i originální návrhy od AI, která se pak díky tomu může jednoduše z datasetu učit. Vymintování NFT probíhá na straně serveru, takže je z pohledu uživatele bez problému a není potřeba další akce.
Základní aplikace vychází z Blazor Server App. Hned ze začátku bylo potřeba přidat prostor pro sdílená data MainDataContext.cs a službu pro načtení a nastartování Neblio účtu, VEDriversLiteCore.cs. V této službě stojí za to vyzdvihnout následující:
using VEDriversLite.AI.OpenAI;
var openAIApiKey = settings.GetValue<string>("OpenAIApiKey", "");
await Console.Out.WriteLineAsync("AI Assistant Inicialization...");
MainDataContext.Assistant = new VirtualAssistant(openAIApiKey);
var init = await MainDataContext.Assistant.InitAssistant();
if (init.Item1)
await Console.Out.WriteLineAsync("AI Assistant Initialized.");Inicializace StorageDriver Handleru a přidání IPFS driveru:
using VEDriversLite.StorageDriver.StorageDrivers;
using VEDriversLite;
VEDriversLite.StorageDriver.Helpers.IPFSHelpers.GatewayURL = "https://ve-framework.com/ipfs/";
var res = await VEDLDataContext.Storage.AddDriver(new VEDriversLite.StorageDriver.StorageDrivers.Dto.StorageDriverConfigDto()
{
Type = "IPFS",
Name = "BDP",
Location = "Cloud",
ID = "BDP",
IsPublicGateway = true,
IsLocal = false,
ConnectionParams = new StorageDriverConnectionParams()
{
APIUrl = "https://ve-framework.com/",
APIPort = 443,
Secured = false,
GatewayURL = "https://ve-framework.com/ipfs/",
GatewayPort = 443,
}
});Načtení klíčů ze souboru nastavení appsettings.json.
Poznámka: Ukládání klíčů touto formou není ideální, slouží to pouze ke zjednodušení. Pro ostré nasazení je vhodné použít robustnější systém. (AzureKeyVault, apod.)
using VEDriversLite.Dto;
var keys = new List<AccountExportDto>();
settings.GetSection("keys").Bind(keys);
if (keys == null || keys.Count == 0)
{
//log.Error("Missing keys in settigns. Cannot continue without at least one root Neblio account.");
Console.WriteLine("Missing keys in settigns. Cannot continue without at least one root Neblio account.");
return;
}Klíče v appsettings.json jsou zatím provizorním řešením kvůli zjednodušení vývoje, testování, apod. Není to ideální řešení pro ostrý provoz, nicméně při zajištění bezpečnosti přístupu na server se nejedná ani o vyloženě nebezpečný přístup. V appsettings je nyní uložený buď přímo privátní klíč, a nebo je možné zadat zašifrovaný klíč (oboje jako položka EKey). V tu chvíli je potřeba vyplnit i heslo (Password). Privátní klíč je možné získat například z backup souboru z VENFT App, nebo exportem z desktopové Neblio QT Wallet](https://nebl.io/wallets/). Při exportu z VENFT App bude klíč v zašifrované podobě a k jeho dešifrování je potřeba ještě heslo. Neblio QT Wallet jej exportuje v nešifrované podobě příkazem dumpprivkey <neblioaddress>.

Rutina pro načtení účtu je k dispozici zde. V rutině je možné nechat načíst i celý VENFT Backup file. To se provede tak, že se do složky aplikace umístí i Backup soubor z VENFT App, ze kterého se změní název na formát "NeblioAddresa-backup.json". Pokud je stejná adresa zadaná i v keys v appsettings.json, zkusí se načíst. Lépe je to vidět zde.
Službu VEDriversLiteCore.cs je potřeba přidat do builderu aplikace, aby se nastartovala. Proces přidání najdete zde.
Jako další krok bylo vytvoření hlavního kontroleru API, HomeController.cs. Nyní obsahuje jen několik funkcí:
Toto téma rozebírá tento článek, který popisuje jak fungují funkce pro vytvoření Textu a obsahu NFT pomocí AI. V kontroleru jsou takové funkce v podstatě jen wrapnuté do funkcí, které je vystavují ven na API.
Trochu odlišná je funkce pro mintování NFT. Ta totiž obsahuje i upload originálních textů od AI. Jedná se o tuto část:
data.additionalinfo.OrigUserBaseText = data.additionalinfo.OrigUserBaseText.Replace("%0A", "\n");
data.additionalinfo.OrigAIText = data.additionalinfo.OrigAIText.Replace("%0A", "\n");
var addInfo = JsonConvert.SerializeObject(data.additionalinfo);
var file = System.Text.Encoding.UTF8.GetBytes(addInfo);
using (Stream ms = new MemoryStream(file))
{
ms.Seek(0, SeekOrigin.Begin);
var result = await VEDLDataContext.Storage.SaveFileToIPFS(new VEDriversLite.StorageDriver.StorageDrivers.Dto.WriteStreamRequestDto()
{
Data = ms,
Filename = "additionalInfo.json",
DriverType = VEDriversLite.StorageDriver.StorageDrivers.StorageDriverType.IPFS,
BackupInLocal = false
});
if (result.Item1)
{
var hash = IPFSHelpers.GetHashFromIPFSLink(result.Item2);
nft.DataItems.Add(new NFTDataItem()
{
Hash = hash,
Storage = DataItemStorageType.IPFS,
Type = DataItemType.JSON,
TagsList = new List<string>() { "AdditionalInfo" }
});
}
}Další postup je dost podobný jako ve výše uvedeném článku, pouze s jednou odlišností - účet je uložený ve slovníku v rámci VEDLDataContextu:
using VEDriversLite;
(bool, string) res = (false, string.Empty);
if (VEDLDataContext.Accounts.TryGetValue(MainDataContext.MainAccount, out var account))
{
res = await account.MintNFT(nft, data.receiver);
await Task.Delay(500);
var tnft = await NFTFactory.GetNFT(NFTHelpers.TokenId, res.Item2, 0, 0, true);
if (tnft != null)
MainDataContext.MintedNFTs.TryAdd(tnft.Utxo, tnft);
}
else
res.Item2 = "Cannot find MainAccount.";Protože se jedná o typ ConcurrentDictionary, je tím částečně zajištěno, že dva klienti by neměli přistoupit k účtu najednou. Nicméně není zde ošetřena odpověď klientovi, že má počkat, nebo zařazení na "čekací listinu". To bude doplněno postupně.
Co se týká kontroleru API, to poslední, co stojí za zmínku, je přidání Swagger API balíčku:
dotnet add package Swashbuckle.AspNetCore--version 6.5.0
dotnet add package Swashbuckle.AspNetCore.Swagger --version 6.5.0
A jeho přidání do aplikace v Program.cs:
// v builder části
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// a v části app
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MintFreeForAI API V1");
});Poté je možné mít k dispozici testovací stránku na volání API příkazů:

Dále je dobré vědět, že je potřeba přidat linky na externí zdroje pro CSS a JavaScripty pro Blazorise a VEBlazor. To se u Blazor Server App dělá v souboru _Layout.cshtml.
V _Imports.razor je vhodné doplnit následující knihovny:
@using VEBlazor.Demo.AI.MintFreeForAI
@using VEBlazor.Demo.AI.MintFreeForAI.Shared
@using VEBlazor.Demo.AI.MintFreeForAI.Components
@using VEFramework.VEBlazor
@using VEFramework.VEBlazor.Pages
@using VEFramework.VEBlazor.Layouts
@using VEFramework.VEBlazor.Menu
@using VEFramework.VEBlazor.Components.Minting
@using Blazorise
@using Blazorise.Components
@using Blazorise.DataGrid
@using Blazorise.Markdown
@using BlazorPanzoomV App.razor bylo potřeba doplnit kód pro nastavení VEBlazor, podobně jako u VENFT App a dalších demo aplikací.
Hlavní logika aplikace je v tuto chvíli v hlavních dvou stránkách:
Na hlavní stránce (Index), je možnost vytvořit NFT pomocí AI. Jedná se vesměs o volání příkazů API po kliknutí na tlačítko. Nicméně za zmínku stojí alespoň využití NFTDataItem.TagsList na uložení hodnocení jednotlivých DataItem položek, v případě obrázků, které AI vytvořila a uživatel hodnotí jejich přesnost, apod. V praxi to u seznamu obrázků vypadá následovně:

Na Index stránce se ještě postupně kolektují originální infomace od AI + Base story od uživatele, které pak můžou sloužit pro porovnání v rámci učení (AI vygenerované vs. uživatelem upravené / doplněné).
Objekt pro zpracování těchto informací se jmenuje AdditionalInfo:
public class AdditionalInfo
{
public string OrigUserBaseText { get; set; } = string.Empty;
public string OrigAIText { get; set; } = string.Empty;
public string OrigAIName { get; set; } = string.Empty;
public string OrigAIDescription { get; set; } = string.Empty;
public string OrigAITags { get; set; } = string.Empty;
}Tento objekt se posílá na API při požadavku na mintování, jak bylo popsáno výše.
Na hlavní stránce je možné také vyplnit Neblio Blockchain adresu, kam si může uživatel nechat poslat NFT. Logika je taková, že pokud nevyplní nic, NFT se vymintuje na Main adrese, a pokud adresu vyplní, pošle se NFT tam.

Validace Neblio adresy probíhá automaticky díky využití komponenty NeblioAddressInput.razor z VEBlazor.
Po vymintování vidí uživatel na stránce odkaz na transakci v Neblio Blockchain Exploreru (tam to trvá cca 30-60s, než se transakce zobrazí), nebo ji může pomocí Search stránky najít v podstatě okamžitě a touto formou i sdílet. Proto slouží tlačítko na kopírování odkazu zároveň ke sdílení:

Zde je odkaz na toto vzorové NFT
Druhá stránka (Search) je ještě jednodušší. Slouží k načtení obsahu NFT a jeho zobrazení. Id transakce je možné zadat buď do vyhledávacího řádku:

nebo do URL:
kde parametr txid je Id NFT Transakce. V kódu se získává automaticky díky Blazoru. Stačí jen parametr opatřit doplňujícím atributem:
[Parameter]
[SupplyParameterFromQuery(Name = "txid")]
public string NFTTxId {get;set;} = string.Empty;Skvělé na tom je, že můžete mít velmi jednoduchou cestou jiný název parametru a proměnné. Blazor zajistí načtení hodnoty, pokud je parametr poskytnut v URL. Je však potřeba zajistit načtení akce, pokud je v NFTTxId nějaký text. Proto se musí přepsat OnAfterRenderAsync funkce:
protected override async Task OnAfterRenderAsync(bool firstLoad)
{
if (firstLoad)
{
if (!string.IsNullOrEmpty(NFTTxId))
await LoadNFT();
}
}Search stránka zobrazuje i originální data od AI. Ty byly při mintování uloženy do formy NFTDataItem, tedy do formy souboru, který je uložený na uložišti, v tomto případě se jedná o IPFS.
Načtení souboru a jeho deserializace probíhá následovně:
using Newtonsoft.Json;
using VEDriversLite;
var it = NFT.DataItems.FirstOrDefault(i => i.Type == DataItemType.JSON);
if (it != null)
{
var file = await VEDLDataContext.Storage.GetFileFromIPFS(new VEDriversLite.StorageDriver.StorageDrivers.Dto.ReadFileRequestDto()
{
Hash = it.Hash,
DriverType = VEDriversLite.StorageDriver.StorageDrivers.StorageDriverType.IPFS
});
if (file.Item1)
{
var info = System.Text.Encoding.UTF8.GetString(file.Item2);
try
{
var parsed = JsonConvert.DeserializeObject<AdditionalInfo>(info);
if (parsed != null)
additionalInfo = parsed;
}
catch (Exception ex)
{
Console.WriteLine("Cannot parse the additional info: " + ex.Message);
}
}
}Zde se spoléhá na to, že je v galerii DataItems jen jedna JSON položka. Jinak by tento algoritmus použil vždy jen tu, na kterou narazí jako první. Pokud by se nejednalo o JSON položku obsahující objekt AdditionalInfo, nepokračoval by dál.

To nejdůležitější ohledně dema v článku zaznělo. Postupně projde refaktoringem a dočká se i doplnění ještě několika funkcí.