diff --git a/Blockly/blockly_human_test.html b/Blockly/blockly_human_test.html
new file mode 100644
index 0000000..abb91e7
--- /dev/null
+++ b/Blockly/blockly_human_test.html
@@ -0,0 +1,108 @@
+
+
+
+
+ Blockly zu Umgangssprache - Mit Schleifen & Zählern
+
+
+
+
+Mein "Umgangssprache"-Generator
+Bauen Sie links Ihre Blöcke. Rechts erscheint die textliche Beschreibung.
+
+
+
+
+
+
+
+
+
+
Erzeugter Text:
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GoogleGeminiAccess/ApiKey.pas b/GoogleGeminiAccess/ApiKey.pas
new file mode 100644
index 0000000..699e5ae
--- /dev/null
+++ b/GoogleGeminiAccess/ApiKey.pas
@@ -0,0 +1,9 @@
+unit ApiKey;
+
+interface
+
+const Value = 'AIzaSyALccoMxn0_wcHswavhu5rzdglLeH6gVlI';
+
+implementation
+
+end.
diff --git a/GoogleGeminiAccess/GeminiAccess.dpr b/GoogleGeminiAccess/GeminiAccess.dpr
new file mode 100644
index 0000000..6212a23
--- /dev/null
+++ b/GoogleGeminiAccess/GeminiAccess.dpr
@@ -0,0 +1,16 @@
+program GeminiAccess;
+
+uses
+ FastMM5,
+ Vcl.Forms,
+ MainUnit in 'MainUnit.pas' {Form1},
+ ApiKey in 'ApiKey.pas';
+
+{$R *.res}
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskbar := True;
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
diff --git a/GoogleGeminiAccess/GeminiAccess.dproj b/GoogleGeminiAccess/GeminiAccess.dproj
new file mode 100644
index 0000000..92536ab
--- /dev/null
+++ b/GoogleGeminiAccess/GeminiAccess.dproj
@@ -0,0 +1,1130 @@
+
+
+ {B5DEDC9F-4F29-41B9-97D4-2AB0ABC43DC1}
+ 20.3
+ VCL
+ True
+ Debug
+ Win64
+ GeminiAccess
+ 3
+ Application
+ GeminiAccess.dpr
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ .\$(Platform)\$(Config)
+ .\$(Platform)\$(Config)
+ false
+ false
+ false
+ false
+ false
+ System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)
+ $(BDS)\bin\delphi_PROJECTICON.ico
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
+ GeminiAccess
+ T:\Myc\Src;$(DCC_UnitSearchPath)
+ 1031
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+
+
+ vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;vclx;Skia.Package.RTL;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;vcltouch;fmxase;dbrtl;Skia.Package.FMX;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)
+ Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
+ Debug
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+ 1033
+ $(BDS)\bin\default_app.manifest
+
+
+ vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;vcltouch;fmxase;dbrtl;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)
+ Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
+ Debug
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+ 1033
+ $(BDS)\bin\default_app.manifest
+
+
+ DEBUG;$(DCC_Define)
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+
+
+ false
+ PerMonitorV2
+
+
+ PerMonitorV2
+ true
+ 1033
+
+
+ false
+ RELEASE;$(DCC_Define)
+ 0
+ 0
+
+
+ PerMonitorV2
+
+
+ PerMonitorV2
+
+
+
+ MainSource
+
+
+
+
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+ Cfg_2
+ Base
+
+
+
+ Delphi.Personality.12
+ Application
+
+
+
+ GeminiAccess.dpr
+
+
+
+
+
+ GeminiAccess.exe
+ true
+
+
+
+
+ GeminiAccess.rsm
+ true
+
+
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ 0
+
+
+
+
+ res\xml
+ 1
+
+
+ res\xml
+ 1
+
+
+
+
+ library\lib\armeabi
+ 1
+
+
+ library\lib\armeabi
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+
+
+ library\lib\mips
+ 1
+
+
+ library\lib\mips
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+ library\lib\arm64-v8a
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable-anydpi-v21
+ 1
+
+
+ res\drawable-anydpi-v21
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+ res\values
+ 1
+
+
+
+
+ res\values-v21
+ 1
+
+
+ res\values-v21
+ 1
+
+
+
+
+ res\values-v31
+ 1
+
+
+ res\values-v31
+ 1
+
+
+
+
+ res\values-v35
+ 1
+
+
+ res\values-v35
+ 1
+
+
+
+
+ res\drawable-anydpi-v26
+ 1
+
+
+ res\drawable-anydpi-v26
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable-anydpi-v33
+ 1
+
+
+ res\drawable-anydpi-v33
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+ res\values
+ 1
+
+
+
+
+ res\values-night-v21
+ 1
+
+
+ res\values-night-v21
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+
+
+ res\drawable-xxxhdpi
+ 1
+
+
+ res\drawable-xxxhdpi
+ 1
+
+
+
+
+ res\drawable-ldpi
+ 1
+
+
+ res\drawable-ldpi
+ 1
+
+
+
+
+ res\drawable-mdpi
+ 1
+
+
+ res\drawable-mdpi
+ 1
+
+
+
+
+ res\drawable-hdpi
+ 1
+
+
+ res\drawable-hdpi
+ 1
+
+
+
+
+ res\drawable-xhdpi
+ 1
+
+
+ res\drawable-xhdpi
+ 1
+
+
+
+
+ res\drawable-mdpi
+ 1
+
+
+ res\drawable-mdpi
+ 1
+
+
+
+
+ res\drawable-hdpi
+ 1
+
+
+ res\drawable-hdpi
+ 1
+
+
+
+
+ res\drawable-xhdpi
+ 1
+
+
+ res\drawable-xhdpi
+ 1
+
+
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+
+
+ res\drawable-xxxhdpi
+ 1
+
+
+ res\drawable-xxxhdpi
+ 1
+
+
+
+
+ res\drawable-small
+ 1
+
+
+ res\drawable-small
+ 1
+
+
+
+
+ res\drawable-normal
+ 1
+
+
+ res\drawable-normal
+ 1
+
+
+
+
+ res\drawable-large
+ 1
+
+
+ res\drawable-large
+ 1
+
+
+
+
+ res\drawable-xlarge
+ 1
+
+
+ res\drawable-xlarge
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+ res\values
+ 1
+
+
+
+
+ res\drawable-anydpi-v24
+ 1
+
+
+ res\drawable-anydpi-v24
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable-night-anydpi-v21
+ 1
+
+
+ res\drawable-night-anydpi-v21
+ 1
+
+
+
+
+ res\drawable-anydpi-v31
+ 1
+
+
+ res\drawable-anydpi-v31
+ 1
+
+
+
+
+ res\drawable-night-anydpi-v31
+ 1
+
+
+ res\drawable-night-anydpi-v31
+ 1
+
+
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ 0
+
+
+
+
+ Contents\MacOS
+ 1
+ .framework
+
+
+ Contents\MacOS
+ 1
+ .framework
+
+
+ Contents\MacOS
+ 1
+ .framework
+
+
+ 0
+
+
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ 0
+ .dll;.bpl
+
+
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ 0
+ .bpl
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ Contents\Resources\StartUp\
+ 0
+
+
+ Contents\Resources\StartUp\
+ 0
+
+
+ Contents\Resources\StartUp\
+ 0
+
+
+ 0
+
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+
+
+ ..\
+ 1
+
+
+ ..\
+ 1
+
+
+ ..\
+ 1
+
+
+
+
+ Contents
+ 1
+
+
+ Contents
+ 1
+
+
+ Contents
+ 1
+
+
+
+
+ Contents\Resources
+ 1
+
+
+ Contents\Resources
+ 1
+
+
+ Contents\Resources
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+ library\lib\arm64-v8a
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ 0
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+
+
+ ..\
+ 1
+
+
+ ..\
+ 1
+
+
+ ..\
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen
+ 64
+
+
+ ..\$(PROJECTNAME).launchscreen
+ 64
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ Assets
+ 1
+
+
+ Assets
+ 1
+
+
+
+
+ Assets
+ 1
+
+
+ Assets
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+ ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+
+
+ 12
+
+
+
+
+
diff --git a/GoogleGeminiAccess/GeminiAccess.res b/GoogleGeminiAccess/GeminiAccess.res
new file mode 100644
index 0000000..ca976d8
Binary files /dev/null and b/GoogleGeminiAccess/GeminiAccess.res differ
diff --git a/GoogleGeminiAccess/MainUnit.dfm b/GoogleGeminiAccess/MainUnit.dfm
new file mode 100644
index 0000000..1c10b26
--- /dev/null
+++ b/GoogleGeminiAccess/MainUnit.dfm
@@ -0,0 +1,52 @@
+object Form1: TForm1
+ Left = 0
+ Top = 0
+ Caption = 'Form1'
+ ClientHeight = 1011
+ ClientWidth = 1105
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = 'Segoe UI'
+ Font.Style = []
+ OnCreate = FormCreate
+ OnDestroy = FormDestroy
+ DesignSize = (
+ 1105
+ 1011)
+ TextHeight = 15
+ object AnswerMemo: TMemo
+ Left = 0
+ Top = 0
+ Width = 1105
+ Height = 809
+ Anchors = [akLeft, akTop, akRight, akBottom]
+ ScrollBars = ssVertical
+ TabOrder = 0
+ end
+ object ExecButton: TButton
+ Left = 992
+ Top = 928
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'Execute'
+ TabOrder = 1
+ OnClick = ExecButtonClick
+ end
+ object PromptMemo: TMemo
+ Left = 0
+ Top = 815
+ Width = 1105
+ Height = 89
+ Anchors = [akLeft, akRight, akBottom]
+ ScrollBars = ssVertical
+ TabOrder = 2
+ end
+ object ApplicationEvents: TApplicationEvents
+ OnIdle = ApplicationEventsIdle
+ Left = 136
+ Top = 104
+ end
+end
diff --git a/GoogleGeminiAccess/MainUnit.pas b/GoogleGeminiAccess/MainUnit.pas
new file mode 100644
index 0000000..654426c
--- /dev/null
+++ b/GoogleGeminiAccess/MainUnit.pas
@@ -0,0 +1,204 @@
+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;
+ FChatHistory: TList; // 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.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.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('candidates')
+ .Items[0].GetValue('content')
+ .GetValue('parts')
+ .Items[0].GetValue('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.
+
diff --git a/Src/Myc.Trade.DataStream.pas b/Src/Myc.Trade.DataStream.pas
index ba582f2..3959b46 100644
--- a/Src/Myc.Trade.DataStream.pas
+++ b/Src/Myc.Trade.DataStream.pas
@@ -5,7 +5,7 @@ unit Myc.Trade.DataStream;
Provides a data server for loading time-series data and streaming it.
This unit contains the core components for handling historical data:
- - TDataRecord: A generic record for a single time-stamped data entry.
+ - TAuraFileDataRecord: A generic record for a single time-stamped data entry.
- IDataServer/TAuraDataServer: A server component responsible for loading and caching
series of data files from disk. Each server instance manages its own cache,
but all instances share a central load gate.
@@ -29,11 +29,6 @@ uses
Myc.Trade.DataPoint;
type
- // A generic record for a single time-stamped data entry.
- TDataRecord = packed record
- TimeStamp: TDateTime;
- Data: T;
- end;
// Represents a generic data stream capable of providing sequential data chunks.
IDataStream = interface
@@ -69,10 +64,16 @@ type
procedure ClearCache;
end;
+ // A generic record for a single time-stamped data entry.
+ TAuraFileDataRecord = packed record
+ TimeStamp: TDateTime;
+ Data: T;
+ end;
+
IAuraDataServer = interface(IDataServer)
['{481E27DE-DC58-49AF-B8A8-B6316980C423}']
- function LoadDataFile(const FileName: string): TFuture>>;
- function LoadDataSeries(const FileName: string): TFuture>>;
+ function LoadDataFile(const FileName: string): TFuture>>;
+ function LoadDataSeries(const FileName: string): TFuture>>;
function EnumerateAssetFiles: TArray;
end;
@@ -87,7 +88,7 @@ type
Name: String;
Age: TDateTime;
LastUsed: TDateTime;
- Data: TFuture>>;
+ Data: TFuture>>;
end;
private
// The cache is per-instance.
@@ -101,8 +102,8 @@ type
FLoadGate: TLatch;
protected
- class function ReadCompressedData(const InputStream: TStream): TArray>; static;
- class function ReadUncompressedData(const InputStream: TStream): TArray>; static;
+ class function ReadCompressedData(const InputStream: TStream): TArray>; static;
+ class function ReadUncompressedData(const InputStream: TStream): TArray>; static;
public
constructor Create(const APath: String);
@@ -123,8 +124,8 @@ type
procedure ClearCache;
function EnumerateAssetFiles: TArray;
- function LoadDataFile(const FileName: string): TFuture>>;
- function LoadDataSeries(const FileName: string): TFuture>>;
+ function LoadDataFile(const FileName: string): TFuture>>;
+ function LoadDataSeries(const FileName: string): TFuture>>;
property Path: String read GetPath;
end;
@@ -135,9 +136,9 @@ type
FDataServer: TAuraDataServer;
FIsLiveData: TMutable.IWriteable;
FCurrentFileName: string;
- FCurrentData: TFuture>>;
+ FCurrentData: TFuture>>;
FNextFileName: string;
- FNextData: TFuture>>;
+ FNextData: TFuture>>;
FCurrPosInFile: Int64;
FCurrentIdx: Int64;
FLastTimeStamp: TDateTime;
@@ -314,12 +315,12 @@ begin
Result := FPath;
end;
-function TAuraDataServer.LoadDataFile(const FileName: string): TFuture>>;
+function TAuraDataServer.LoadDataFile(const FileName: string): TFuture>>;
var
parsedPath, parsedSymbol: string;
parsedYear, parsedMonth: Integer;
begin
- Result := TFuture>>.Null;
+ Result := TFuture>>.Null;
if not TryParseFileName(FileName, parsedPath, parsedSymbol, parsedYear, parsedMonth) then
exit;
@@ -368,8 +369,8 @@ begin
.Construct(
TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate
function: TBytes begin Result := TFile.ReadAllBytes(capFileName); end)
- .Chain>>(
- function(bytes: TBytes): TArray>
+ .Chain>>(
+ function(bytes: TBytes): TArray>
begin
var zipMemoryStream := TBytesStream.Create(bytes);
try
@@ -383,9 +384,9 @@ begin
else if TFile.Exists(FileName) then
begin
Result :=
- TFuture>>.Construct(
+ TFuture>>.Construct(
TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate
- function: TArray>
+ function: TArray>
begin
if TFile.Exists(capFileName) then
begin
@@ -418,11 +419,11 @@ begin
end;
end;
-function TAuraDataServer.LoadDataSeries(const FileName: string): TFuture>>;
+function TAuraDataServer.LoadDataSeries(const FileName: string): TFuture>>;
var
loadedState: TState;
- loadedFiles: TArray>>>;
- liveData: TFuture>>;
+ loadedFiles: TArray>>>;
+ liveData: TFuture>>;
tabFiles: TList;
currentFile: string;
liveFilePath: string;
@@ -452,7 +453,7 @@ begin
end;
end;
- var loadedFileList := TList>>>.Create;
+ var loadedFileList := TList>>>.Create;
var loadStates := TList.Create;
try
for currentFile in tabFiles do
@@ -462,7 +463,7 @@ begin
loadStates.Add(data.Done);
end;
- liveData := TFuture>>.Null;
+ liveData := TFuture>>.Null;
if liveFilePath <> '' then
begin
liveData := LoadDataFile(liveFilePath);
@@ -481,11 +482,11 @@ begin
end;
Result :=
- TFuture>>.Construct(
+ TFuture>>.Construct(
loadedState,
- function: TArray>
+ function: TArray>
begin
- var tickList := TList>.Create;
+ var tickList := TList>.Create;
try
var overallLastTabTickTime: TDateTime := 0;
var cnt := 0;
@@ -508,7 +509,7 @@ begin
);
end;
-class function TAuraDataServer.ReadCompressedData(const InputStream: TStream): TArray>;
+class function TAuraDataServer.ReadCompressedData(const InputStream: TStream): TArray>;
var
decompressionStream: TStream;
localHeader: TZipHeader;
@@ -547,7 +548,7 @@ begin
end;
end;
-class function TAuraDataServer.ReadUncompressedData(const InputStream: TStream): TArray>;
+class function TAuraDataServer.ReadUncompressedData(const InputStream: TStream): TArray>;
var
fileSize: Int64;
recordCount, bytesRead: Integer;
@@ -555,10 +556,10 @@ begin
SetLength(Result, 0);
InputStream.Position := 0;
fileSize := InputStream.Size;
- if (fileSize = 0) or ((fileSize mod SizeOf(TDataRecord)) <> 0) then
+ if (fileSize = 0) or ((fileSize mod SizeOf(TAuraFileDataRecord)) <> 0) then
exit;
- recordCount := fileSize div SizeOf(TDataRecord);
+ recordCount := fileSize div SizeOf(TAuraFileDataRecord);
if recordCount > 0 then
begin
SetLength(Result, recordCount);
@@ -596,8 +597,8 @@ end;
function TAuraFileStream.GetChunk(var Data: array of TDataPoint): Integer;
var
- item: TDataRecord;
- currData: TArray>;
+ item: TAuraFileDataRecord;
+ currData: TArray>;
begin
Result := 0;
if not FCurrentData.Done.IsSet then