229 lines
7.5 KiB
ObjectPascal
229 lines
7.5 KiB
ObjectPascal
unit MainUnit;
|
|
|
|
interface
|
|
|
|
uses
|
|
Winapi.Windows,
|
|
Winapi.Messages,
|
|
System.SysUtils,
|
|
System.Variants,
|
|
System.Classes,
|
|
Vcl.Graphics,
|
|
Vcl.Controls,
|
|
Vcl.Forms,
|
|
Vcl.Dialogs,
|
|
Vcl.StdCtrls,
|
|
System.Net.HttpClient,
|
|
System.Net.HttpClientComponent,
|
|
System.JSON,
|
|
System.Net.Mime,
|
|
Vcl.AppEvnts,
|
|
Myc.Futures,
|
|
System.Generics.Collections,
|
|
ApiKey;
|
|
|
|
type
|
|
// NEU: Ein Record, um einen einzelnen Redebeitrag in der Konversation zu speichern
|
|
TChatTurn = record
|
|
Role: string; // 'user' oder 'model'
|
|
Text: string;
|
|
end;
|
|
|
|
TForm1 = class(TForm)
|
|
AnswerMemo: TMemo;
|
|
ExecButton: TButton;
|
|
ApplicationEvents: TApplicationEvents;
|
|
PromptMemo: TMemo;
|
|
procedure ExecButtonClick(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
|
|
private
|
|
FHttpClient: TNetHTTPClient;
|
|
FMyApiKey: string;
|
|
FAnswer: TFuture<String>;
|
|
FChatHistory: TList<TChatTurn>; // NEU: Liste für den Konversationsverlauf
|
|
procedure SendPrompt;
|
|
public
|
|
{ Public declarations }
|
|
end;
|
|
|
|
var
|
|
Form1: TForm1;
|
|
|
|
implementation
|
|
|
|
{$R *.dfm}
|
|
|
|
const
|
|
GEMINI_MODEL = 'gemini-2.5-flash-preview-05-20';
|
|
API_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/models/';
|
|
|
|
{ TForm1 }
|
|
|
|
procedure TForm1.FormCreate(Sender: TObject);
|
|
begin
|
|
FHttpClient := TNetHTTPClient.Create(nil);
|
|
FMyApiKey := ApiKey.Value; // Eingelesen aus ApiKey.pas
|
|
|
|
// NEU: Initialisiert die Liste für den Konversationsverlauf
|
|
FChatHistory := TList<TChatTurn>.Create;
|
|
|
|
// Optional: Füge hier einen initialen System-Prompt hinzu, wenn gewünscht.
|
|
// Dieser wird dann immer als erste Nachricht mitgesendet.
|
|
// var initialTurn: TChatTurn;
|
|
// initialTurn.Role := 'user';
|
|
// initialTurn.Text := 'Du bist ein Delphi-Entwicklungs-Assistent...';
|
|
// FChatHistory.Add(initialTurn);
|
|
end;
|
|
|
|
procedure TForm1.FormDestroy(Sender: TObject);
|
|
begin
|
|
FAnswer.WaitFor;
|
|
FHttpClient.Free;
|
|
FChatHistory.Free; // NEU: Gibt die Verlaufsliste frei
|
|
end;
|
|
|
|
procedure TForm1.ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
|
|
var
|
|
answerText: string;
|
|
modelTurn: TChatTurn;
|
|
begin
|
|
if FAnswer.Done.IsSet and not ExecButton.Enabled then
|
|
begin
|
|
answerText := FAnswer.Value;
|
|
AnswerMemo.Lines.Add(answerText);
|
|
|
|
// NEU: Füge die Antwort des Modells zum Verlauf hinzu
|
|
if not answerText.StartsWith('Fehler!') then
|
|
begin
|
|
modelTurn.Role := 'model';
|
|
modelTurn.Text := answerText;
|
|
FChatHistory.Add(modelTurn);
|
|
end;
|
|
|
|
FAnswer := FAnswer.Null;
|
|
ExecButton.Enabled := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.ExecButtonClick(Sender: TObject);
|
|
begin
|
|
SendPrompt;
|
|
end;
|
|
|
|
procedure TForm1.SendPrompt;
|
|
var
|
|
userPrompt: string;
|
|
userTurn: TChatTurn;
|
|
begin
|
|
if not ExecButton.Enabled then
|
|
Exit;
|
|
|
|
userPrompt := PromptMemo.Lines.Text;
|
|
if userPrompt.IsEmpty then
|
|
begin
|
|
ShowMessage('Bitte geben Sie einen Prompt ein.');
|
|
Exit;
|
|
end;
|
|
|
|
// NEU: Füge die aktuelle Benutzereingabe zum Verlauf hinzu
|
|
userTurn.Role := 'user';
|
|
userTurn.Text := userPrompt;
|
|
FChatHistory.Add(userTurn);
|
|
|
|
// NEU: Leere das Eingabefeld für die nächste Nachricht
|
|
PromptMemo.Clear;
|
|
|
|
ExecButton.Enabled := False;
|
|
AnswerMemo.Lines.Add('');
|
|
AnswerMemo.Lines.Add('--- [USER] ---');
|
|
AnswerMemo.Lines.Add(userPrompt);
|
|
AnswerMemo.Lines.Add('... sende Anfrage an Gemini ...');
|
|
|
|
FAnswer :=
|
|
TFuture<string>.Construct(
|
|
function: string
|
|
var
|
|
jsonRequest: TJSONObject;
|
|
jsonContents: TJSONArray;
|
|
requestBody: TStringStream;
|
|
response: IHTTPResponse;
|
|
responseJson: TJSONValue;
|
|
apiUrl: string;
|
|
turn: TChatTurn;
|
|
begin
|
|
try
|
|
// 1. JSON-Body aus dem gesamten Konversationsverlauf erstellen
|
|
jsonRequest := TJSONObject.Create;
|
|
try
|
|
jsonContents := TJSONArray.Create;
|
|
jsonRequest.AddPair('contents', jsonContents);
|
|
|
|
// NEU: Iteriere durch den Verlauf und baue die JSON-Struktur auf
|
|
for turn in FChatHistory do
|
|
begin
|
|
var jsonTurn := TJSONObject.Create;
|
|
jsonTurn.AddPair('role', TJSONString.Create(turn.Role));
|
|
|
|
var jsonParts := TJSONArray.Create;
|
|
jsonTurn.AddPair('parts', jsonParts);
|
|
|
|
var jsonText := TJSONObject.Create;
|
|
jsonText.AddPair('text', TJSONString.Create(turn.Text));
|
|
jsonParts.Add(jsonText);
|
|
|
|
jsonContents.Add(jsonTurn);
|
|
end;
|
|
|
|
// 2. Request senden
|
|
requestBody := TStringStream.Create(jsonRequest.ToString, TEncoding.UTF8);
|
|
try
|
|
apiUrl := API_BASE_URL + GEMINI_MODEL + ':generateContent?key=' + FMyApiKey;
|
|
response := FHttpClient.Post(apiUrl, requestBody, nil);
|
|
|
|
// 3. Antwort verarbeiten
|
|
if (response.StatusCode = 200) then
|
|
begin
|
|
responseJson := TJSONObject.ParseJSONValue(response.ContentAsString(TEncoding.UTF8));
|
|
try
|
|
Result :=
|
|
responseJson
|
|
.GetValue<TJSONArray>('candidates')
|
|
.Items[0]
|
|
.GetValue<TJSONObject>('content')
|
|
.GetValue<TJSONArray>('parts')
|
|
.Items[0]
|
|
.GetValue<string>('text');
|
|
finally
|
|
responseJson.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Result :=
|
|
'Fehler! Status: '
|
|
+ response.StatusCode.ToString
|
|
+ ' - '
|
|
+ response.StatusText
|
|
+ sLineBreak
|
|
+ response.ContentAsString;
|
|
end;
|
|
finally
|
|
requestBody.Free;
|
|
end;
|
|
finally
|
|
jsonRequest.Free;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := '--- FEHLER IM TASK ---' + sLineBreak + E.Message;
|
|
end;
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
end.
|