226 lines
6.5 KiB
Plaintext
226 lines
6.5 KiB
Plaintext
unit uKandinskyAPI;
|
||
|
||
interface
|
||
|
||
uses
|
||
System.SysUtils, System.Classes, System.JSON, System.Net.HttpClient,
|
||
System.Net.URLClient, System.NetConsts, system.StrUtils, System.Net.Mime,
|
||
System.NetEncoding, System.Threading;
|
||
|
||
type
|
||
TGenerationDoneEvent = procedure(Sender: TObject; const FileName: string) of object;
|
||
TStatusUpdateEvent = procedure(Sender: TObject; const Message: string) of object;
|
||
TErrorEvent = procedure(Sender: TObject; const ErrorMessage: string) of object;
|
||
|
||
TFusionBrainAPI = class(TComponent)
|
||
private
|
||
FBaseURL: string;
|
||
FApiKey: string;
|
||
FSecretKey: string;
|
||
FClient: THTTPClient;
|
||
FOnGenerationDone: TGenerationDoneEvent;
|
||
FOnStatusUpdate: TStatusUpdateEvent;
|
||
FOnError: TErrorEvent;
|
||
|
||
procedure DoStatusUpdate(const AMessage: string);
|
||
procedure DoGenerationDone(const AFileName: string);
|
||
procedure DoError(const AErrorMessage: string);
|
||
function GetAuthHeaders: TNetHeaders;
|
||
function GetPipeline: string;
|
||
function Generate(const Prompt, PipelineId: string): string;
|
||
function CheckGeneration(const RequestId: string): TArray<string>;
|
||
procedure SaveBase64Image(const Base64Str, FileName: string);
|
||
public
|
||
constructor Create(AOwner: TComponent; aKey:string; aSecret:string);
|
||
destructor Destroy; override;
|
||
procedure StartGeneration(const APrompt: string);
|
||
|
||
property OnGenerationDone: TGenerationDoneEvent read FOnGenerationDone write FOnGenerationDone;
|
||
property OnStatusUpdate: TStatusUpdateEvent read FOnStatusUpdate write FOnStatusUpdate;
|
||
property OnError: TErrorEvent read FOnError write FOnError;
|
||
end;
|
||
|
||
implementation
|
||
|
||
uses ugeneral;
|
||
|
||
constructor TFusionBrainAPI.Create(AOwner: TComponent; aKey:string; aSecret:string);
|
||
begin
|
||
inherited Create(AOwner);
|
||
FClient := THTTPClient.Create;
|
||
FBaseURL := 'https://api-key.fusionbrain.ai/';
|
||
FApiKey :=aKey;
|
||
FSecretKey := aSecret;
|
||
end;
|
||
|
||
destructor TFusionBrainAPI.Destroy;
|
||
begin
|
||
FClient.Free;
|
||
inherited;
|
||
end;
|
||
|
||
procedure TFusionBrainAPI.StartGeneration(const APrompt: string);
|
||
var
|
||
Task: ITask;
|
||
begin
|
||
Task :=TTask.Run(procedure
|
||
var
|
||
PipelineID, UUID, FileName: string;
|
||
Links: TArray<string>;
|
||
begin
|
||
try
|
||
TThread.Queue(nil, procedure begin DoStatusUpdate('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...'); end);
|
||
PipelineID := GetPipeline;
|
||
|
||
TThread.Queue(nil, procedure begin DoStatusUpdate('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...'); end);
|
||
UUID := Generate(APrompt, PipelineID);
|
||
|
||
TThread.Queue(nil, procedure begin DoStatusUpdate('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...'); end);
|
||
Links := CheckGeneration(UUID);
|
||
|
||
FileName := myConst.AppDataPath + 'imgs\kandinsky_' + FormatDateTime('yyyymmddhhnnss', Now) + '.jpg';
|
||
SaveBase64Image(Links[0], FileName);
|
||
|
||
TThread.Queue(nil, procedure begin DoGenerationDone(FileName); end);
|
||
except
|
||
on E: Exception do
|
||
TThread.Queue(nil, procedure begin DoError(E.Message); end);
|
||
end;
|
||
end);
|
||
end;
|
||
|
||
function TFusionBrainAPI.GetAuthHeaders: TNetHeaders;
|
||
begin
|
||
SetLength(Result, 2);
|
||
Result[0] := TNetHeader.Create('X-Key', 'Key ' + FApiKey);
|
||
Result[1] := TNetHeader.Create('X-Secret', 'Secret ' + FSecretKey);
|
||
|
||
end;
|
||
|
||
function TFusionBrainAPI.GetPipeline: string;
|
||
var
|
||
Response: IHTTPResponse;
|
||
Json: TJSONArray;
|
||
begin
|
||
Response := FClient.Get(FBaseURL + 'key/api/v1/pipelines', nil, GetAuthHeaders);
|
||
Json := TJSONObject.ParseJSONValue(Response.ContentAsString) as TJSONArray;
|
||
try
|
||
Result := Json.Items[0].GetValue<string>('id');
|
||
finally
|
||
Json.Free;
|
||
end;
|
||
end;
|
||
|
||
function TFusionBrainAPI.Generate(const Prompt, PipelineId: string): string;
|
||
var
|
||
Params, Root: TJSONObject;
|
||
Multipart: TMultipartFormData;
|
||
Response: IHTTPResponse;
|
||
Json: TJSONObject;
|
||
begin
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> JSON
|
||
Params := TJSONObject.Create;
|
||
Root := TJSONObject.Create;
|
||
try
|
||
Params.AddPair('query', Prompt);
|
||
Root.AddPair('type', 'GENERATE');
|
||
Root.AddPair('numImages', TJSONNumber.Create(1));
|
||
Root.AddPair('width', TJSONNumber.Create(512));
|
||
Root.AddPair('height', TJSONNumber.Create(512));
|
||
Root.AddPair('generateParams', Params);
|
||
|
||
Multipart := TMultipartFormData.Create;
|
||
try
|
||
Multipart.AddField('pipeline_id', PipelineId);
|
||
Multipart.AddField('params', Root.ToString, 'application/json');
|
||
|
||
Response := FClient.Post(FBaseURL + 'key/api/v1/pipeline/run', Multipart, nil, GetAuthHeaders);
|
||
Json := TJSONObject.ParseJSONValue(Response.ContentAsString) as TJSONObject;
|
||
try
|
||
Result := Json.GetValue<string>('uuid');
|
||
finally
|
||
Json.Free;
|
||
end;
|
||
finally
|
||
Multipart.Free;
|
||
end;
|
||
finally
|
||
Root.Free;
|
||
// Params <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> Root
|
||
end;
|
||
end;
|
||
|
||
function TFusionBrainAPI.CheckGeneration(const RequestId: string): TArray<string>;
|
||
const
|
||
MaxAttempts = 60;
|
||
var
|
||
Response: IHTTPResponse;
|
||
Json, ResultObj: TJSONObject;
|
||
Files: TJSONArray;
|
||
i, Attempt: Integer;
|
||
Status: string;
|
||
begin
|
||
Attempt := 0;
|
||
while Attempt < MaxAttempts do
|
||
begin
|
||
Sleep(5000);
|
||
Inc(Attempt);
|
||
|
||
Response := FClient.Get(FBaseURL + 'key/api/v1/pipeline/status/' + RequestId, nil, GetAuthHeaders);
|
||
Json := TJSONObject.ParseJSONValue(Response.ContentAsString) as TJSONObject;
|
||
try
|
||
Status := Json.GetValue<string>('status');
|
||
if Status = 'DONE' then
|
||
begin
|
||
ResultObj := Json.GetValue<TJSONObject>('result');
|
||
Files := ResultObj.GetValue<TJSONArray>('files');
|
||
SetLength(Result, Files.Count);
|
||
for i := 0 to Files.Count - 1 do
|
||
Result[i] := Files.Items[i].Value;
|
||
Exit;
|
||
end
|
||
else if Status = 'FAILED' then
|
||
raise Exception.Create('Generation failed');
|
||
finally
|
||
Json.Free;
|
||
end;
|
||
end;
|
||
raise Exception.Create('Timeout waiting for generation');
|
||
end;
|
||
|
||
procedure TFusionBrainAPI.SaveBase64Image(const Base64Str, FileName: string);
|
||
var
|
||
DecodedStream: TMemoryStream;
|
||
InputStr: TStringStream;
|
||
begin
|
||
DecodedStream := TMemoryStream.Create;
|
||
InputStr := TStringStream.Create(Base64Str, TEncoding.ASCII);
|
||
try
|
||
TNetEncoding.Base64.Decode(InputStr, DecodedStream);
|
||
DecodedStream.SaveToFile(FileName);
|
||
finally
|
||
DecodedStream.Free;
|
||
InputStr.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure TFusionBrainAPI.DoStatusUpdate(const AMessage: string);
|
||
begin
|
||
if Assigned(FOnStatusUpdate) then
|
||
FOnStatusUpdate(Self, AMessage);
|
||
end;
|
||
|
||
procedure TFusionBrainAPI.DoGenerationDone(const AFileName: string);
|
||
begin
|
||
if Assigned(FOnGenerationDone) then
|
||
FOnGenerationDone(Self, AFileName);
|
||
end;
|
||
|
||
procedure TFusionBrainAPI.DoError(const AErrorMessage: string);
|
||
begin
|
||
if Assigned(FOnError) then
|
||
FOnError(Self, AErrorMessage);
|
||
end;
|
||
|
||
end.
|