From a62ba89bca43947608cbb6c276e6407ee6ad534d Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Thu, 8 Jan 2026 14:13:41 +0100 Subject: [PATCH] Gemini Test --- GeminiTest/GeminiTest.dpr | 16 + GeminiTest/GeminiTest.dproj | 1159 +++++++++++++++++++++++++++++++ GeminiTest/GeminiTest.res | Bin 0 -> 154804 bytes GeminiTest/MainUnit.fmx | 187 +++++ GeminiTest/MainUnit.pas | 347 +++++++++ GeminiTest/Myc.Api.Gemini.pas | 822 ++++++++++++++++++++++ GeminiTest/Myc.System.Quota.pas | 254 +++++++ Src/AST/Myc.Ast.Json.Schema.pas | 138 ++-- 8 files changed, 2879 insertions(+), 44 deletions(-) create mode 100644 GeminiTest/GeminiTest.dpr create mode 100644 GeminiTest/GeminiTest.dproj create mode 100644 GeminiTest/GeminiTest.res create mode 100644 GeminiTest/MainUnit.fmx create mode 100644 GeminiTest/MainUnit.pas create mode 100644 GeminiTest/Myc.Api.Gemini.pas create mode 100644 GeminiTest/Myc.System.Quota.pas diff --git a/GeminiTest/GeminiTest.dpr b/GeminiTest/GeminiTest.dpr new file mode 100644 index 0000000..3b14376 --- /dev/null +++ b/GeminiTest/GeminiTest.dpr @@ -0,0 +1,16 @@ +program GeminiTest; + +uses + System.StartUpCopy, + FMX.Forms, + MainUnit in 'MainUnit.pas' {Form1}, + Myc.Api.Gemini in 'Myc.Api.Gemini.pas', + Myc.System.Quota in 'Myc.System.Quota.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/GeminiTest/GeminiTest.dproj b/GeminiTest/GeminiTest.dproj new file mode 100644 index 0000000..5b1aeaa --- /dev/null +++ b/GeminiTest/GeminiTest.dproj @@ -0,0 +1,1159 @@ + + + {C61FC33B-44CC-44B8-81A4-C2ABEC37C72F} + 20.3 + FMX + True + Debug + Win64 + GeminiTest + 2 + Application + GeminiTest.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;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + GeminiTest + + + 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;fmxase;vcltouch;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 + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + 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;fmxase;vcltouch;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 + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + true + true + + + false + PerMonitorV2 + + + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + PerMonitorV2 + + + PerMonitorV2 + + + + MainSource + + +
Form1
+ fmx +
+ + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + Application + + + + GeminiTest.dpr + + + + + + true + + + + + true + + + + + true + + + + + GeminiTest.exe + true + + + + + GeminiTest.exe + true + + + + + GeminiTest.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 + + + + + + + + + + + + + + + + + False + False + False + False + False + False + False + False + False + True + + + 12 + + + + +
diff --git a/GeminiTest/GeminiTest.res b/GeminiTest/GeminiTest.res new file mode 100644 index 0000000000000000000000000000000000000000..e356e03cb33684368c249c849baae659cc458e50 GIT binary patch literal 154804 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 V*DvDwi0kl4AN*nNN9*Eu{|{V6%J2XH literal 0 HcmV?d00001 diff --git a/GeminiTest/MainUnit.fmx b/GeminiTest/MainUnit.fmx new file mode 100644 index 0000000..9610ee3 --- /dev/null +++ b/GeminiTest/MainUnit.fmx @@ -0,0 +1,187 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'Form1' + ClientHeight = 708 + ClientWidth = 906 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + OnCreate = FormCreate + DesignerMasterStyle = 0 + object AskButton: TButton + Anchors = [akRight, akBottom] + Position.X = 800.000000000000000000 + Position.Y = 577.000000000000000000 + TabOrder = 1 + Text = 'Ask' + OnClick = AskButtonClick + end + object Edit1: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + Anchors = [akLeft, akBottom] + TabOrder = 2 + Text = 'AIzaSyBiWN55O-lI36EXcBeqf_cne4jAzxNsfbg' + Position.X = 8.000000000000000000 + Position.Y = 678.000000000000000000 + Size.Width = 145.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + OnExit = Edit1Exit + end + object ModelsComboBox: TComboBox + Anchors = [akLeft, akBottom] + Position.X = 8.000000000000000000 + Position.Y = 577.000000000000000000 + Size.Width = 193.000000000000000000 + Size.Height = 22.000000000000000000 + Size.PlatformDefault = False + TabOrder = 3 + end + object AskMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + Anchors = [akLeft, akRight, akBottom] + Position.X = 224.000000000000000000 + Position.Y = 577.000000000000000000 + Size.Width = 568.000000000000000000 + Size.Height = 123.000000000000000000 + Size.PlatformDefault = False + TabOrder = 5 + Viewport.Width = 564.000000000000000000 + Viewport.Height = 119.000000000000000000 + end + object ResetChatButton: TButton + Anchors = [akRight, akBottom] + Position.X = 800.000000000000000000 + Position.Y = 645.000000000000000000 + TabOrder = 6 + Text = 'Reset' + OnClick = ResetChatButtonClick + end + object TabControl1: TTabControl + Anchors = [akLeft, akTop, akRight, akBottom] + Size.Width = 906.000000000000000000 + Size.Height = 561.000000000000000000 + Size.PlatformDefault = False + TabIndex = 0 + TabOrder = 7 + TabPosition = PlatformDefault + Sizes = ( + 906s + 535s + 906s + 535s + 906s + 535s) + object ChatTabItem: TTabItem + CustomIcon = < + item + end> + IsSelected = True + Size.Width = 45.000000000000000000 + Size.Height = 26.000000000000000000 + Size.PlatformDefault = False + StyleLookup = '' + TabOrder = 0 + Text = 'Chat' + object ChatMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + Align = Client + Size.Width = 906.000000000000000000 + Size.Height = 535.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + Viewport.Width = 902.000000000000000000 + Viewport.Height = 531.000000000000000000 + end + end + object CacheTabItem: TTabItem + CustomIcon = < + item + end> + IsSelected = False + Size.Width = 53.000000000000000000 + Size.Height = 26.000000000000000000 + Size.PlatformDefault = False + StyleLookup = '' + TabOrder = 0 + Text = 'Cache' + object CacheMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + Align = Client + Size.Width = 906.000000000000000000 + Size.Height = 535.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + Viewport.Width = 902.000000000000000000 + Viewport.Height = 531.000000000000000000 + end + end + object SysInstrTabItem: TTabItem + CustomIcon = < + item + end> + IsSelected = False + Size.Width = 61.000000000000000000 + Size.Height = 26.000000000000000000 + Size.PlatformDefault = False + StyleLookup = '' + TabOrder = 0 + Text = 'SysInstr' + object SystemInstructionMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + TextSettings.WordWrap = True + Align = Client + Size.Width = 906.000000000000000000 + Size.Height = 535.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + Viewport.Width = 902.000000000000000000 + Viewport.Height = 531.000000000000000000 + end + end + end + object UploadCacheButton: TButton + Anchors = [akRight, akBottom] + Position.X = 800.000000000000000000 + Position.Y = 675.000000000000000000 + TabOrder = 0 + Text = 'Upload' + OnClick = UploadCacheButtonClick + end + object ThinkingCheckBox: TCheckBox + Anchors = [akRight, akBottom] + Position.X = 800.000000000000000000 + Position.Y = 607.000000000000000000 + Size.Width = 88.000000000000000000 + Size.Height = 19.000000000000000000 + Size.PlatformDefault = False + TabOrder = 8 + Text = 'Thinking' + end + object GlobalQuoteLabel: TLabel + Anchors = [akLeft, akBottom] + Position.X = 8.000000000000000000 + Position.Y = 608.000000000000000000 + Size.Width = 193.000000000000000000 + Size.Height = 17.000000000000000000 + Size.PlatformDefault = False + Text = 'Global Quota: 0' + TabOrder = 9 + end + object QuotaLabel: TLabel + Anchors = [akRight, akBottom] + Position.X = 704.000000000000000000 + Position.Y = 537.000000000000000000 + Size.Width = 194.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + TextSettings.HorzAlign = Trailing + Text = 'in/out' + TabOrder = 4 + end +end diff --git a/GeminiTest/MainUnit.pas b/GeminiTest/MainUnit.pas new file mode 100644 index 0000000..595d392 --- /dev/null +++ b/GeminiTest/MainUnit.pas @@ -0,0 +1,347 @@ +unit MainUnit; + +interface + +uses + System.SysUtils, + System.Types, + System.UITypes, + System.Classes, + System.Variants, + FMX.Types, + FMX.Controls, + FMX.Forms, + FMX.Graphics, + FMX.Dialogs, + FMX.Memo.Types, + FMX.ScrollBox, + FMX.Memo, + FMX.Controls.Presentation, + FMX.StdCtrls, + FMX.Edit, + FMX.ListBox, + Myc.Api.Gemini, + FMX.TabControl; + +type + TForm1 = class(TForm) + AskButton: TButton; + Edit1: TEdit; + ModelsComboBox: TComboBox; + QuotaLabel: TLabel; + AskMemo: TMemo; + ResetChatButton: TButton; + TabControl1: TTabControl; + ChatTabItem: TTabItem; + CacheTabItem: TTabItem; + SysInstrTabItem: TTabItem; + ChatMemo: TMemo; + CacheMemo: TMemo; + SystemInstructionMemo: TMemo; + UploadCacheButton: TButton; + ThinkingCheckBox: TCheckBox; + GlobalQuoteLabel: TLabel; + procedure FormCreate(Sender: TObject); + procedure AskButtonClick(Sender: TObject); + procedure Edit1Exit(Sender: TObject); + procedure ResetChatButtonClick(Sender: TObject); + procedure UploadCacheButtonClick(Sender: TObject); + private + FChat: IGeminiChat; + // Holds the client configured with the active cache + FCachedClient: IGeminiClient; + procedure InitChat; + procedure UpdateQuotaDisplay; + public + procedure GetAvailableModels; + end; + +var + Form1: TForm1; + +implementation + +uses + System.Threading, + Myc.System.Quota; // Einbindung für den globalen Zähler + +{$R *.fmx} + +const + FallbackModel = 'gemini-3-flash-preview'; + +procedure TForm1.FormCreate(Sender: TObject); +begin + // Load default instructions if file exists + if FileExists('T:\Myc\KI\gemini.md') then + SystemInstructionMemo.Lines.LoadFromFile('T:\Myc\KI\gemini.md', TEncoding.UTF8); + + // Initialen Global-Stand anzeigen + UpdateQuotaDisplay; + + GetAvailableModels; + InitChat; +end; + +procedure TForm1.InitChat; +begin + if Assigned(FCachedClient) then + begin + FChat := FCachedClient.StartChat; + ChatMemo.Lines.Add('--- New Chat Session (Cached Content Active) ---'); + end + else + begin + FChat := nil; // Lazy init in AskButton + ChatMemo.Lines.Add('--- New Chat Session ---'); + end; +end; + +procedure TForm1.ResetChatButtonClick(Sender: TObject); +begin + InitChat; +end; + +procedure TForm1.UpdateQuotaDisplay; +begin + GlobalQuoteLabel.Text := 'Total: ' + TGlobalQuota.GetTotal.ToString + ', Today: ' + TGlobalQuota.GetDailyTotal.ToString; +end; + +procedure TForm1.AskButtonClick(Sender: TObject); +var + apiKey: string; + modelName: string; + prompt: string; +begin + apiKey := Edit1.Text; + prompt := AskMemo.Text; + + if ModelsComboBox.ItemIndex > -1 then + modelName := ModelsComboBox.Items[ModelsComboBox.ItemIndex] + else + modelName := FallbackModel; + + // --- LAZY INIT --- + if FChat = nil then + begin + var client: IGeminiClient := TGeminiClient.Create(apiKey, modelName); + + // 1. System Instructions (Ohne Hack, purer Text) + if not SystemInstructionMemo.Text.Trim.IsEmpty then + client.SystemInstruction := SystemInstructionMemo.Text; + + // 2. Native Thinking aktivieren + // Die API-Unit entscheidet, ob das Modell es unterstützt. + // Falls ja, wird der 'thinking' Parameter gesendet. + client.EnableThinking := ThinkingCheckBox.IsChecked; + + FChat := client.StartChat; + end; + + AskButton.Enabled := False; + ChatMemo.Lines.Add(sLineBreak + 'User: "' + prompt + '"'); + ChatMemo.Lines.Add('Gemini: '); + AskMemo.Text := ''; + + TTask.Run( + procedure + var + CurrentChat: IGeminiChat; + resultData: TGeminiResult; + begin + CurrentChat := FChat; + if CurrentChat = nil then + exit; + + // --- STREAMING --- + resultData := + CurrentChat.SendMessageStream( + prompt, + procedure(const TextChunk: string; IsThought: Boolean) + begin + TThread.Queue( + nil, + procedure + begin + if IsThought then + begin + // Visuelle Kennzeichnung für Gedanken + // (Hier einfach in eckigen Klammern, besser wäre Farbe) + ChatMemo.Text := ChatMemo.Text + ' [Think: ' + TextChunk + '] '; + end + else + begin + // Normale Antwort + ChatMemo.Text := ChatMemo.Text + TextChunk; + end; + + ChatMemo.GoToTextEnd; + end + ); + end + ); + + // Abschluss im Main Thread + TThread.Queue( + nil, + procedure + begin + if resultData.Text.StartsWith('Error') then + ChatMemo.Lines.Add(resultData.Text); + + // Update Labels (Session & Global Registry) + QuotaLabel.Text := Format('Input: %d | Output: %d', [resultData.Usage.PromptTokens, resultData.Usage.CandidatesTokens]); + UpdateQuotaDisplay; + + AskButton.Enabled := True; + end + ); + end + ); +end; + +procedure TForm1.Edit1Exit(Sender: TObject); +begin + GetAvailableModels; +end; + +procedure TForm1.GetAvailableModels; +var + apiKey: string; +begin + apiKey := Edit1.Text.Trim; + if apiKey.IsEmpty then + exit; + + ChatMemo.Lines.Add('Fetching available models...'); + + TTask.Run( + procedure + var + client: IGeminiClient; + modelList: TArray; + begin + client := TGeminiClient.Create(apiKey); + modelList := client.ListModels; + + TThread.Queue( + nil, + procedure + var + mName: string; + begin + if Length(modelList) = 0 then + begin + ChatMemo.Lines.Add('No models found.'); + exit; + end; + + ModelsComboBox.BeginUpdate; + try + ModelsComboBox.Items.Clear; + + for mName in modelList do + ModelsComboBox.Items.Add(mName); + + // Versuche Fallback zu finden (ggf. ohne Emoji suffix match) + // Vereinfacht: Wähle ersten Index + if ModelsComboBox.Items.Count > 0 then + begin + ModelsComboBox.ItemIndex := ModelsComboBox.Items.IndexOf(FallbackModel); + if ModelsComboBox.ItemIndex < 0 then + ModelsComboBox.ItemIndex := 0; + end; + + ChatMemo.Lines.Add(Format('Loaded %d models.', [Length(modelList)])); + finally + ModelsComboBox.EndUpdate; + end; + end + ); + end + ); +end; + +procedure TForm1.UploadCacheButtonClick(Sender: TObject); +var + apiKey: string; + content: string; + sysInstr: string; + modelName: string; +begin + apiKey := Edit1.Text; + content := CacheMemo.Text; + + // Keine künstlichen Prompts mehr, nur die reinen Instruktionen + sysInstr := SystemInstructionMemo.Text; + + if content.Trim.IsEmpty then + begin + ChatMemo.Lines.Add('Error: Cache content is empty.'); + exit; + end; + + // Remove Emoji suffix if present from ComboBox for API call + if ModelsComboBox.ItemIndex > -1 then + modelName := ModelsComboBox.Items[ModelsComboBox.ItemIndex].Split([' '])[0] + else + modelName := FallbackModel; + + UploadCacheButton.Enabled := False; + ChatMemo.Lines.Add('Creating Cache (this may take a moment)...'); + + TTask.Run( + procedure + var + client: IGeminiClient; + cacheName: string; + errMsg: string; + begin + try + // 1. Client erstellen + client := TGeminiClient.Create(apiKey, modelName); + + // 2. Cache erstellen (Content + SysInstr) + // Hinweis: Native Thinking ist ein Parameter der GENERIERUNG, nicht des Caches. + // Daher laden wir den Cache "neutral" hoch. + cacheName := client.CreateCache(content, sysInstr, 300); + + // 3. Cache aktivieren + client.ActiveCacheName := cacheName; + + // 4. Client speichern + // Wenn der User später "Ask" klickt, wird dort EnableThinking gesetzt + FCachedClient := client; + + TThread.Queue( + nil, + procedure + begin + ChatMemo.Lines.Add('Success: Cache created!'); + ChatMemo.Lines.Add('ID: ' + cacheName); + + InitChat; + UploadCacheButton.Enabled := True; + end + ); + + except + on E: Exception do + begin + errMsg := E.Message; + TThread.Queue( + nil, + procedure + begin + ChatMemo.Lines.Add('Cache Upload Failed:'); + ChatMemo.Lines.Add(errMsg); + UploadCacheButton.Enabled := True; + end + ); + end; + end; + end + ); +end; + +end. diff --git a/GeminiTest/Myc.Api.Gemini.pas b/GeminiTest/Myc.Api.Gemini.pas new file mode 100644 index 0000000..596958b --- /dev/null +++ b/GeminiTest/Myc.Api.Gemini.pas @@ -0,0 +1,822 @@ +unit Myc.Api.Gemini; + +interface + +uses + System.SysUtils, + System.Classes, + System.Net.HttpClient, + System.Net.Mime, + System.JSON, + System.NetEncoding, + System.Generics.Collections, + System.SyncObjs, + Myc.System.Quota; // Einbindung der Global Quota Unit + +type + // Represents usage statistics for a request + TGeminiUsage = record + PromptTokens: Integer; + CandidatesTokens: Integer; + TotalTokens: Integer; + end; + + // Represents the result of a generation request + TGeminiResult = record + Text: string; + IsThought: Boolean; // True if this part contains reasoning/thoughts + Usage: TGeminiUsage; + end; + + TChatRole = (User, Model); + + TChatItem = record + Role: TChatRole; + Text: string; + end; + + // Callback for streaming: provides the new text delta and thought flag + TStreamCallback = reference to procedure(const TextDelta: string; IsThought: Boolean); + + // Interface for a chat session (maintains history) + IGeminiChat = interface + ['{5C2F64C8-4D18-47C0-9513-332715053673}'] + function SendMessage(const Text: string): TGeminiResult; + function SendMessageStream(const Text: string; const OnDelta: TStreamCallback): TGeminiResult; + function GetHistory: TArray; + end; + + // Interface for the API Client + IGeminiClient = interface + ['{E6721670-3498-4660-848D-82F3B1A2B5E2}'] + // Standard Generation + function GenerateContent(const Prompt: string): TGeminiResult; overload; + function GenerateContent(const History: TArray): TGeminiResult; overload; + + // Streaming Generation + function GenerateContentStream(const Prompt: string; const OnDelta: TStreamCallback): TGeminiResult; overload; + function GenerateContentStream(const History: TArray; const OnDelta: TStreamCallback): TGeminiResult; overload; + + // Utilities + function ListModels: TArray; + function StartChat: IGeminiChat; + + // Context Caching + function CreateCache(const Content: string; const SysInstructions: string; const TTLSeconds: Integer = 300): string; + procedure SetActiveCache(const CacheName: string); + function GetActiveCache: string; + + // Configuration + procedure SetSystemInstruction(const Value: string); + function GetSystemInstruction: string; + + // Native Thinking / Reasoning + procedure SetEnableThinking(const Value: Boolean); + function GetEnableThinking: Boolean; + + property ActiveCacheName: string read GetActiveCache write SetActiveCache; + property SystemInstruction: string read GetSystemInstruction write SetSystemInstruction; + property EnableThinking: Boolean read GetEnableThinking write SetEnableThinking; + end; + + // Implementation of the API Client + TGeminiClient = class(TInterfacedObject, IGeminiClient) + private + class var + FTotalUsage: TGeminiUsage; + private + FApiKey: string; + FBaseUrl: string; + FStreamUrl: string; + FListUrl: string; + FModelName: string; + FActiveCacheName: string; + FSystemInstruction: string; + FEnableThinking: Boolean; + + function ParseResponse(const JsonStr: string): TGeminiResult; + function ParseModels(const JsonStr: string): TArray; + function BuildJsonBody(const History: TArray): TJSONObject; + procedure ProcessStreamData(const Data: TBytes; var Buffer: string; const OnDelta: TStreamCallback; var TotalResult: TGeminiResult); + public + constructor Create(const AApiKey: string; const AModel: string = 'gemini-1.5-flash-001'); + + function GenerateContent(const Prompt: string): TGeminiResult; overload; + function GenerateContent(const History: TArray): TGeminiResult; overload; + + function GenerateContentStream(const Prompt: string; const OnDelta: TStreamCallback): TGeminiResult; overload; + function GenerateContentStream(const History: TArray; const OnDelta: TStreamCallback): TGeminiResult; overload; + + function ListModels: TArray; + function StartChat: IGeminiChat; + + function CreateCache(const Content: string; const SysInstructions: string; const TTLSeconds: Integer = 300): string; + procedure SetActiveCache(const CacheName: string); + function GetActiveCache: string; + + procedure SetSystemInstruction(const Value: string); + function GetSystemInstruction: string; + + procedure SetEnableThinking(const Value: Boolean); + function GetEnableThinking: Boolean; + + // Static access to session-global usage (RAM only) + class property TotalUsage: TGeminiUsage read FTotalUsage; + end; + +implementation + +type + // Internal Chat Session Implementation + TChatSession = class(TInterfacedObject, IGeminiChat) + private + FClient: IGeminiClient; + FHistory: TList; + public + constructor Create(const Client: IGeminiClient); + destructor Destroy; override; + function SendMessage(const Text: string): TGeminiResult; + function SendMessageStream(const Text: string; const OnDelta: TStreamCallback): TGeminiResult; + function GetHistory: TArray; + end; + +{ TGeminiClient } + +constructor TGeminiClient.Create(const AApiKey: string; const AModel: string); +var + LModel: string; + encodedKey: string; +begin + inherited Create; + FApiKey := AApiKey.Trim; + LModel := AModel.Trim; + + if LModel.IsEmpty then + LModel := 'gemini-1.5-flash-001'; + + FModelName := LModel; + if not FModelName.StartsWith('models/') then + FModelName := 'models/' + FModelName; + + encodedKey := TNetEncoding.URL.EncodeQuery(FApiKey); + + // Endpoint construction + FBaseUrl := Format('https://generativelanguage.googleapis.com/v1beta/%s:generateContent?key=%s', [FModelName, encodedKey]); + + FStreamUrl := Format('https://generativelanguage.googleapis.com/v1beta/%s:streamGenerateContent?key=%s', [FModelName, encodedKey]); + + FListUrl := Format('https://generativelanguage.googleapis.com/v1beta/models?key=%s', [encodedKey]); + + FEnableThinking := False; +end; + +// --- Property Setters/Getters --- + +procedure TGeminiClient.SetEnableThinking(const Value: Boolean); +begin + FEnableThinking := Value; +end; + +function TGeminiClient.GetEnableThinking: Boolean; +begin + Result := FEnableThinking; +end; + +procedure TGeminiClient.SetActiveCache(const CacheName: string); +begin + FActiveCacheName := CacheName; +end; + +function TGeminiClient.GetActiveCache: string; +begin + Result := FActiveCacheName; +end; + +procedure TGeminiClient.SetSystemInstruction(const Value: string); +begin + FSystemInstruction := Value; +end; + +function TGeminiClient.GetSystemInstruction: string; +begin + Result := FSystemInstruction; +end; + +// --- Core Logic --- + +function TGeminiClient.GenerateContent(const Prompt: string): TGeminiResult; +var + item: TChatItem; +begin + item.Role := TChatRole.User; + item.Text := Prompt; + Result := GenerateContent([item]); +end; + +function TGeminiClient.GenerateContent(const History: TArray): TGeminiResult; +var + httpClient: THTTPClient; + requestBody: TJSONObject; + response: IHTTPResponse; + stringStream: TStringStream; +begin + Result.Text := ''; + Result.Usage := Default(TGeminiUsage); + Result.IsThought := False; + + httpClient := THTTPClient.Create; + requestBody := BuildJsonBody(History); + try + stringStream := TStringStream.Create(requestBody.ToString, TEncoding.UTF8); + try + httpClient.CustomHeaders['Content-Type'] := 'application/json'; + httpClient.ConnectionTimeout := 10000; + httpClient.ResponseTimeout := 30000; + + try + response := httpClient.Post(FBaseUrl, stringStream); + except + on E: Exception do + begin + Result.Text := 'Error: Connection failed - ' + E.Message; + exit; + end; + end; + + if (response.StatusCode = 200) then + begin + Result := ParseResponse(response.ContentAsString(TEncoding.UTF8)); + + // Update Usages + if (Result.Usage.TotalTokens > 0) then + begin + // RAM Counter + TInterlocked.Add(FTotalUsage.PromptTokens, Result.Usage.PromptTokens); + TInterlocked.Add(FTotalUsage.CandidatesTokens, Result.Usage.CandidatesTokens); + TInterlocked.Add(FTotalUsage.TotalTokens, Result.Usage.TotalTokens); + + // Persistent System Counter + TGlobalQuota.AddTokens(Result.Usage.TotalTokens); + end; + end + else + begin + Result.Text := + Format('Error: %d - %s (%s)', [response.StatusCode, response.StatusText, response.ContentAsString(TEncoding.UTF8)]); + end; + finally + stringStream.Free; + end; + finally + httpClient.Free; + requestBody.Free; + end; +end; + +function TGeminiClient.GenerateContentStream(const Prompt: string; const OnDelta: TStreamCallback): TGeminiResult; +var + item: TChatItem; +begin + item.Role := TChatRole.User; + item.Text := Prompt; + Result := GenerateContentStream([item], OnDelta); +end; + +function TGeminiClient.GenerateContentStream(const History: TArray; const OnDelta: TStreamCallback): TGeminiResult; +var + httpClient: THTTPClient; + requestBody: TJSONObject; + stringStream: TStringStream; + buffer: string; + + // Captured local variable for the result state to avoid E2555 + LFullResult: TGeminiResult; +begin + // Init local result container + LFullResult.Text := ''; + LFullResult.Usage := Default(TGeminiUsage); + LFullResult.IsThought := False; + buffer := ''; + + httpClient := THTTPClient.Create; + requestBody := BuildJsonBody(History); + try + stringStream := TStringStream.Create(requestBody.ToString, TEncoding.UTF8); + try + httpClient.CustomHeaders['Content-Type'] := 'application/json'; + httpClient.ConnectionTimeout := 10000; + httpClient.ResponseTimeout := 60000; + + // Use ReceiveDataExCallback to access raw data chunks efficiently + httpClient.ReceiveDataExCallback := + procedure( + const Sender: TObject; + AContentLength: Int64; + AReadCount: Int64; + AChunk: Pointer; + AChunkLength: Cardinal; + var AAbort: Boolean + ) + var + LBytes: TBytes; + begin + if AChunkLength > 0 then + begin + SetLength(LBytes, AChunkLength); + Move(AChunk^, LBytes[0], AChunkLength); + + ProcessStreamData(LBytes, buffer, OnDelta, LFullResult); + end; + end; + + try + httpClient.Post(FStreamUrl, stringStream); + except + on E: Exception do + begin + LFullResult.Text := LFullResult.Text + sLineBreak + '[Error: ' + E.Message + ']'; + end; + end; + + // Update Usages + if LFullResult.Usage.TotalTokens > 0 then + begin + // RAM Counter + TInterlocked.Add(FTotalUsage.PromptTokens, LFullResult.Usage.PromptTokens); + TInterlocked.Add(FTotalUsage.CandidatesTokens, LFullResult.Usage.CandidatesTokens); + TInterlocked.Add(FTotalUsage.TotalTokens, LFullResult.Usage.TotalTokens); + + // Persistent System Counter + TGlobalQuota.AddTokens(LFullResult.Usage.TotalTokens); + end; + + finally + stringStream.Free; + end; + finally + httpClient.Free; + requestBody.Free; + end; + + Result := LFullResult; +end; + +// --- JSON Body Construction --- + +function TGeminiClient.BuildJsonBody(const History: TArray): TJSONObject; +var + contentsArray, partsArray: TJSONArray; + contentObj, partObj: TJSONObject; + sysObj, sysPart: TJSONObject; + sysPartsArr: TJSONArray; + item: TChatItem; + genConfig, thinkConfig: TJSONObject; +begin + Result := TJSONObject.Create; + + // 1. Caching Strategy + if not FActiveCacheName.IsEmpty then + begin + Result.AddPair('cachedContent', FActiveCacheName); + end + else + begin + // 2. Dynamic System Instructions (only if no cache active) + if not FSystemInstruction.IsEmpty then + begin + sysObj := TJSONObject.Create; + + sysPartsArr := TJSONArray.Create; + sysPart := TJSONObject.Create; + sysPart.AddPair('text', FSystemInstruction); + sysPartsArr.Add(sysPart); + + sysObj.AddPair('parts', sysPartsArr); + Result.AddPair('systemInstruction', sysObj); + end; + end; + + // 3. Native Thinking Configuration + if FEnableThinking then + begin + thinkConfig := TJSONObject.Create; + thinkConfig.AddPair('includeThoughts', TJSONTrue.Create); + // 'thinkingLevel' omitted to use model default + + genConfig := TJSONObject.Create; + genConfig.AddPair('thinkingConfig', thinkConfig); + + Result.AddPair('generationConfig', genConfig); + end; + + // 4. Chat Contents + contentsArray := TJSONArray.Create; + + for item in History do + begin + contentObj := TJSONObject.Create; + + // Delphi 13 inline if + contentObj.AddPair( + 'role', + if item.Role = TChatRole.User then 'user' + else 'model' + ); + + partObj := TJSONObject.Create; + partObj.AddPair('text', item.Text); + + partsArray := TJSONArray.Create; + partsArray.Add(partObj); + + contentObj.AddPair('parts', partsArray); + contentsArray.Add(contentObj); + end; + Result.AddPair('contents', contentsArray); +end; + +// --- Response Parsing (Streaming) --- + +procedure TGeminiClient.ProcessStreamData( + const Data: TBytes; + var Buffer: string; + const OnDelta: TStreamCallback; + var TotalResult: TGeminiResult +); +var + chunkStr: string; + jsonStart, jsonEnd: Integer; + jsonSub: string; + jsonObj: TJSONObject; + candidates, parts: TJSONArray; + part, usageObj: TJSONObject; + deltaText: string; + isDeltaThought: Boolean; + braceCount, i: Integer; + foundEnd: Boolean; +begin + chunkStr := TEncoding.UTF8.GetString(Data); + Buffer := Buffer + chunkStr; + + // Parse loop to extract complete JSON objects from buffer + while True do + begin + jsonStart := Buffer.IndexOf('{'); + jsonEnd := -1; + if jsonStart < 0 then + break; + + // Brackets matching to find end of JSON object + braceCount := 0; + foundEnd := False; + for i := jsonStart to Buffer.Length - 1 do + begin + if Buffer.Chars[i] = '{' then + inc(braceCount) + else if Buffer.Chars[i] = '}' then + dec(braceCount); + + if (braceCount = 0) then + begin + jsonEnd := i; + foundEnd := True; + break; + end; + end; + + if not foundEnd then + break; // Object incomplete + + jsonSub := Buffer.Substring(jsonStart, jsonEnd - jsonStart + 1); + Buffer := Buffer.Substring(jsonEnd + 1); + + jsonObj := TJSONObject.ParseJSONValue(jsonSub) as TJSONObject; + if Assigned(jsonObj) then + try + deltaText := ''; + isDeltaThought := False; + + candidates := jsonObj.GetValue('candidates') as TJSONArray; + if (Assigned(candidates)) and (candidates.Count > 0) then + begin + var cand := candidates.Items[0] as TJSONObject; + var content := cand.GetValue('content') as TJSONObject; + if Assigned(content) then + begin + parts := content.GetValue('parts') as TJSONArray; + if (Assigned(parts)) and (parts.Count > 0) then + begin + part := parts.Items[0] as TJSONObject; + deltaText := part.GetValue('text'); + + // Check for Native Thinking flag "thought": true + part.TryGetValue('thought', isDeltaThought); + end; + end; + end; + + usageObj := jsonObj.GetValue('usageMetadata') as TJSONObject; + if Assigned(usageObj) then + begin + usageObj.TryGetValue('promptTokenCount', TotalResult.Usage.PromptTokens); + usageObj.TryGetValue('candidatesTokenCount', TotalResult.Usage.CandidatesTokens); + usageObj.TryGetValue('totalTokenCount', TotalResult.Usage.TotalTokens); + end; + + if not deltaText.IsEmpty then + begin + // Append to full text (including thoughts) + if not isDeltaThought then + TotalResult.Text := TotalResult.Text + deltaText; + + // Fire callback + if Assigned(OnDelta) then + OnDelta(deltaText, isDeltaThought); + end; + finally + jsonObj.Free; + end; + end; +end; + +// --- Response Parsing (Synchronous) --- + +function TGeminiClient.ParseResponse(const JsonStr: string): TGeminiResult; +var + jsonObj: TJSONObject; + candidates, parts: TJSONArray; + candidate, content, part, usageObj: TJSONObject; + isThought: Boolean; +begin + Result.Text := ''; + Result.Usage := Default(TGeminiUsage); + Result.IsThought := False; + + jsonObj := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject; + if not Assigned(jsonObj) then + exit; + + try + candidates := jsonObj.GetValue('candidates') as TJSONArray; + if (Assigned(candidates)) and (candidates.Count > 0) then + begin + candidate := candidates.Items[0] as TJSONObject; + content := candidate.GetValue('content') as TJSONObject; + if Assigned(content) then + begin + parts := content.GetValue('parts') as TJSONArray; + if (Assigned(parts)) and (parts.Count > 0) then + begin + part := parts.Items[0] as TJSONObject; + Result.Text := part.GetValue('text'); + + // Check for thought flag + if part.TryGetValue('thought', isThought) then + Result.IsThought := isThought; + end; + end; + end; + + usageObj := jsonObj.GetValue('usageMetadata') as TJSONObject; + if Assigned(usageObj) then + begin + usageObj.TryGetValue('promptTokenCount', Result.Usage.PromptTokens); + usageObj.TryGetValue('candidatesTokenCount', Result.Usage.CandidatesTokens); + usageObj.TryGetValue('totalTokenCount', Result.Usage.TotalTokens); + end; + finally + jsonObj.Free; + end; +end; + +// --- Model Listing --- + +function TGeminiClient.ListModels: TArray; +var + httpClient: THTTPClient; + response: IHTTPResponse; +begin + SetLength(Result, 0); + httpClient := THTTPClient.Create; + try + httpClient.ConnectionTimeout := 10000; + try + response := httpClient.Get(FListUrl); + if (response.StatusCode = 200) then + Result := ParseModels(response.ContentAsString(TEncoding.UTF8)); + except + // Return empty array on error + end; + finally + httpClient.Free; + end; +end; + +function TGeminiClient.ParseModels(const JsonStr: string): TArray; +var + jsonObj: TJSONObject; + models, methods: TJSONArray; + model: TJSONObject; + i, j: Integer; + modelName: string; + canGen: Boolean; + list: TList; +begin + SetLength(Result, 0); + jsonObj := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject; + if not Assigned(jsonObj) then + exit; + + list := TList.Create; + try + models := jsonObj.GetValue('models') as TJSONArray; + if Assigned(models) then + begin + for i := 0 to models.Count - 1 do + begin + model := models.Items[i] as TJSONObject; + modelName := model.GetValue('name'); + + if modelName.StartsWith('models/') then + modelName := modelName.Substring(7); + + // Filter for Gemini models + if not modelName.ToLower.Contains('gemini') then + continue; + if modelName.ToLower.Contains('embedding') then + continue; + if modelName.ToLower.Contains('robotics') then + continue; + if modelName.ToLower.Contains('computer-use') then + continue; + + // Filter for Generation capabilities + canGen := False; + methods := model.GetValue('supportedGenerationMethods') as TJSONArray; + if Assigned(methods) then + for j := 0 to methods.Count - 1 do + if (methods.Items[j].Value = 'generateContent') then + begin + canGen := True; + break; + end; + + if canGen then + list.Add(modelName); + end; + end; + Result := list.ToArray; + finally + list.Free; + jsonObj.Free; + end; +end; + +// --- Context Caching --- + +function TGeminiClient.CreateCache(const Content: string; const SysInstructions: string; const TTLSeconds: Integer): string; +var + httpClient: THTTPClient; + requestBody: TJSONObject; + contentsArray, partsArray: TJSONArray; + contentObj, partObj, sysObj: TJSONObject; + sysPartsArr: TJSONArray; + sysPart: TJSONObject; + response: IHTTPResponse; + stringStream: TStringStream; + cacheUrl: string; + respJson: TJSONObject; +begin + Result := ''; + cacheUrl := Format('https://generativelanguage.googleapis.com/v1beta/cachedContents?key=%s', [TNetEncoding.URL.EncodeQuery(FApiKey)]); + + httpClient := THTTPClient.Create; + requestBody := TJSONObject.Create; + try + requestBody.AddPair('model', FModelName); + + partObj := TJSONObject.Create; + partObj.AddPair('text', Content); + + partsArray := TJSONArray.Create; + partsArray.Add(partObj); + + contentObj := TJSONObject.Create; + contentObj.AddPair('parts', partsArray); + contentObj.AddPair('role', 'user'); + + contentsArray := TJSONArray.Create; + contentsArray.Add(contentObj); + requestBody.AddPair('contents', contentsArray); + + if not SysInstructions.IsEmpty then + begin + sysObj := TJSONObject.Create; + + sysPartsArr := TJSONArray.Create; + sysPart := TJSONObject.Create; + sysPart.AddPair('text', SysInstructions); + sysPartsArr.Add(sysPart); + + sysObj.AddPair('parts', sysPartsArr); + requestBody.AddPair('systemInstruction', sysObj); + end; + + requestBody.AddPair('ttl', IntToStr(TTLSeconds) + 's'); + + stringStream := TStringStream.Create(requestBody.ToString, TEncoding.UTF8); + try + httpClient.CustomHeaders['Content-Type'] := 'application/json'; + try + response := httpClient.Post(cacheUrl, stringStream); + except + on E: Exception do + raise Exception.Create('Cache Creation Failed: ' + E.Message); + end; + + // Success is 200 OK or 201 Created + if (response.StatusCode = 200) or (response.StatusCode = 201) then + begin + respJson := TJSONObject.ParseJSONValue(response.ContentAsString(TEncoding.UTF8)) as TJSONObject; + try + if Assigned(respJson) then + Result := respJson.GetValue('name'); + finally + respJson.Free; + end; + end + else + begin + raise Exception.CreateFmt('Cache Error %d: %s', [response.StatusCode, response.ContentAsString(TEncoding.UTF8)]); + end; + finally + stringStream.Free; + end; + finally + httpClient.Free; + requestBody.Free; + end; +end; + +function TGeminiClient.StartChat: IGeminiChat; +begin + Result := TChatSession.Create(Self); +end; + +// --- TChatSession Implementation --- + +constructor TChatSession.Create(const Client: IGeminiClient); +begin + inherited Create; + FClient := Client; + FHistory := TList.Create; +end; + +destructor TChatSession.Destroy; +begin + FHistory.Free; + inherited; +end; + +function TChatSession.SendMessage(const Text: string): TGeminiResult; +var + item: TChatItem; +begin + item.Role := TChatRole.User; + item.Text := Text; + FHistory.Add(item); + + Result := FClient.GenerateContent(FHistory.ToArray); + + if not Result.Text.IsEmpty and not Result.Text.StartsWith('Error') then + begin + item.Role := TChatRole.Model; + item.Text := Result.Text; + FHistory.Add(item); + end; +end; + +function TChatSession.SendMessageStream(const Text: string; const OnDelta: TStreamCallback): TGeminiResult; +var + item: TChatItem; +begin + item.Role := TChatRole.User; + item.Text := Text; + FHistory.Add(item); + + Result := FClient.GenerateContentStream(FHistory.ToArray, OnDelta); + + if not Result.Text.IsEmpty and not Result.Text.StartsWith('Error') then + begin + item.Role := TChatRole.Model; + item.Text := Result.Text; + FHistory.Add(item); + end; +end; + +function TChatSession.GetHistory: TArray; +begin + Result := FHistory.ToArray; +end; + +end. diff --git a/GeminiTest/Myc.System.Quota.pas b/GeminiTest/Myc.System.Quota.pas new file mode 100644 index 0000000..4afa256 --- /dev/null +++ b/GeminiTest/Myc.System.Quota.pas @@ -0,0 +1,254 @@ +unit Myc.System.Quota; + +interface + +uses + System.SysUtils, + System.Win.Registry, + Winapi.Windows, + System.Classes; + +type + // Handles global quota tracking via Windows Registry + TGlobalQuota = class + private + const + REG_KEY = 'Software\Myc\GeminiTools\Quota'; + + // Value names + const + VAL_TOTAL_TOKENS = 'TotalTokens'; + const + VAL_DAILY_TOKENS = 'DailyTokens'; + const + VAL_LAST_RESET = 'LastReset'; // Manual reset timestamp + const + VAL_LAST_UPDATE = 'LastUpdate'; // Last modification timestamp + const + VAL_DAILY_DATE = 'DailyDate'; // Date for the daily counter reference + + public + // Adds tokens to global and daily counters thread-safely + // Automatically resets the daily counter if the day has changed + class procedure AddTokens(Count: Integer); + + // Reads the all-time total from registry + class function GetTotal: Int64; + + // Reads the accumulated tokens for the current day + class function GetDailyTotal: Int64; + + // Returns the date and time of the last modification (AddTokens) + class function GetLastUpdate: TDateTime; + + // Returns the date and time of the last manual reset + class function GetLastReset: TDateTime; + + // Resets all counters (Total and Daily) to zero + class procedure Reset; + end; + +implementation + +{ TGlobalQuota } + +class procedure TGlobalQuota.AddTokens(Count: Integer); +var + reg: TRegistry; + mutex: THandle; + totalTokens, dailyTokens: Int64; + strValue: string; + lastDailyDate, today: TDateTime; +begin + if Count <= 0 then + exit; + + // Use a named mutex to prevent race conditions between different processes + mutex := CreateMutex(nil, False, 'GlobalGeminiQuotaMutex'); + if WaitForSingleObject(mutex, 2000) = WAIT_OBJECT_0 then + try + reg := TRegistry.Create(KEY_READ or KEY_WRITE); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, True) then + begin + // 1. Handle Total Tokens + totalTokens := 0; + if reg.ValueExists(VAL_TOTAL_TOKENS) then + begin + strValue := reg.ReadString(VAL_TOTAL_TOKENS); + TryStrToInt64(strValue, totalTokens); + end; + totalTokens := totalTokens + Count; + + // 2. Handle Daily Tokens + dailyTokens := 0; + lastDailyDate := 0; + today := Trunc(Now); // Date part only + + if reg.ValueExists(VAL_DAILY_DATE) then + lastDailyDate := reg.ReadDate(VAL_DAILY_DATE); + + // Check if we are on a new day + if lastDailyDate = today then + begin + // Same day, read existing daily count + if reg.ValueExists(VAL_DAILY_TOKENS) then + begin + strValue := reg.ReadString(VAL_DAILY_TOKENS); + TryStrToInt64(strValue, dailyTokens); + end; + end + else + begin + // New day (or first run), dailyTokens remains 0 + // Update the reference date + reg.WriteDate(VAL_DAILY_DATE, today); + end; + + dailyTokens := dailyTokens + Count; + + // 3. Write updates + // Store numbers as String to support Int64 (Registry WriteInteger is 32-bit in older API wrappers) + reg.WriteString(VAL_TOTAL_TOKENS, totalTokens.ToString); + reg.WriteString(VAL_DAILY_TOKENS, dailyTokens.ToString); + reg.WriteDateTime(VAL_LAST_UPDATE, Now); + + reg.CloseKey; + end; + finally + reg.Free; + end; + finally + ReleaseMutex(mutex); + CloseHandle(mutex); + end; +end; + +class function TGlobalQuota.GetTotal: Int64; +var + reg: TRegistry; + strValue: string; +begin + Result := 0; + reg := TRegistry.Create(KEY_READ); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, False) then + begin + if reg.ValueExists(VAL_TOTAL_TOKENS) then + begin + strValue := reg.ReadString(VAL_TOTAL_TOKENS); + TryStrToInt64(strValue, Result); + end; + reg.CloseKey; + end; + finally + reg.Free; + end; +end; + +class function TGlobalQuota.GetDailyTotal: Int64; +var + reg: TRegistry; + strValue: string; + lastDailyDate: TDateTime; +begin + Result := 0; + reg := TRegistry.Create(KEY_READ); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, False) then + begin + // Check if the stored daily count belongs to today + if reg.ValueExists(VAL_DAILY_DATE) then + begin + lastDailyDate := reg.ReadDate(VAL_DAILY_DATE); + if lastDailyDate = Trunc(Now) then + begin + if reg.ValueExists(VAL_DAILY_TOKENS) then + begin + strValue := reg.ReadString(VAL_DAILY_TOKENS); + TryStrToInt64(strValue, Result); + end; + end; + // If date differs, Result remains 0 (logic effectively resets on read, though write happens in AddTokens) + end; + reg.CloseKey; + end; + finally + reg.Free; + end; +end; + +class function TGlobalQuota.GetLastUpdate: TDateTime; +var + reg: TRegistry; +begin + Result := 0; + reg := TRegistry.Create(KEY_READ); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, False) then + begin + if reg.ValueExists(VAL_LAST_UPDATE) then + Result := reg.ReadDateTime(VAL_LAST_UPDATE); + reg.CloseKey; + end; + finally + reg.Free; + end; +end; + +class function TGlobalQuota.GetLastReset: TDateTime; +var + reg: TRegistry; +begin + Result := 0; + reg := TRegistry.Create(KEY_READ); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, False) then + begin + if reg.ValueExists(VAL_LAST_RESET) then + Result := reg.ReadDateTime(VAL_LAST_RESET); + reg.CloseKey; + end; + finally + reg.Free; + end; +end; + +class procedure TGlobalQuota.Reset; +var + reg: TRegistry; + mutex: THandle; +begin + // Reset also needs Mutex protection to avoid conflicting writes with AddTokens + mutex := CreateMutex(nil, False, 'GlobalGeminiQuotaMutex'); + if WaitForSingleObject(mutex, 2000) = WAIT_OBJECT_0 then + try + reg := TRegistry.Create(KEY_WRITE); + try + reg.RootKey := HKEY_CURRENT_USER; + if reg.OpenKey(REG_KEY, True) then + begin + reg.WriteString(VAL_TOTAL_TOKENS, '0'); + reg.WriteString(VAL_DAILY_TOKENS, '0'); + reg.WriteDate(VAL_DAILY_DATE, Trunc(Now)); + reg.WriteDateTime(VAL_LAST_RESET, Now); + // We typically update LastUpdate on reset as well, or leave it as the last "Add" action. + // Here we update it to indicate change. + reg.WriteDateTime(VAL_LAST_UPDATE, Now); + reg.CloseKey; + end; + finally + reg.Free; + end; + finally + ReleaseMutex(mutex); + CloseHandle(mutex); + end; +end; + +end. diff --git a/Src/AST/Myc.Ast.Json.Schema.pas b/Src/AST/Myc.Ast.Json.Schema.pas index 6a031dc..e73bad0 100644 --- a/Src/AST/Myc.Ast.Json.Schema.pas +++ b/Src/AST/Myc.Ast.Json.Schema.pas @@ -47,20 +47,19 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject; Result.AddPair('type', ATypeName); end; - function JConst(const AVal: string): TJSONObject; + // Use enum for const strings as it's widely supported + function JEnum(const AVal: string): TJSONObject; + var + arr: TJSONArray; begin - Result := TJSONObject.Create; - Result.AddPair('const', AVal); + Result := JType('string'); + arr := TJSONArray.Create; + arr.Add(AVal); + Result.AddPair('enum', arr); end; - function JArray(AItems: TJSONValue): TJSONObject; - begin - Result := JType('array'); - Result.AddPair('items', AItems); - end; - - // Combined function: Creates array from items and sets prefixItems + limits - function JFixedArray(const AItems: array of TJSONValue): TJSONObject; + // Tuple Array (Fixed items) + function JTupleArray(const AItems: array of TJSONValue): TJSONObject; var arr: TJSONArray; item: TJSONValue; @@ -68,19 +67,22 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject; begin Result := JType('array'); + // "items" as an array defines a Tuple schema in older drafts (supported by Gemini) arr := TJSONArray.Create; for item in AItems do arr.AddElement(item); count := arr.Count; - Result.AddPair('prefixItems', arr); + Result.AddPair('items', arr); // Items as Array = Tuple Definition Result.AddPair('minItems', TJSONNumber.Create(count)); Result.AddPair('maxItems', TJSONNumber.Create(count)); + Result.AddPair('additionalItems', TJSONBool.Create(False)); // No extra items allowed end; function JTuple(AItems: TJSONValue): TJSONObject; begin - Result := JFixedArray([JConst('Tuple'), AItems]); + // ["Tuple", [...]] + Result := JTupleArray([JEnum('Tuple'), AItems]); end; function JAnyOf(AOptions: array of TJSONValue): TJSONObject; @@ -97,7 +99,7 @@ class function TAstSchema.GetJsonType(Kind: TFieldKind): TJSONObject; {$endregion} var - pair, selItems, selTuple, entryContent, entryNode: TJSONObject; + pair, selTuple, entryNode: TJSONObject; begin case Kind of fkNode: Result := JDefRef('Node'); @@ -105,28 +107,54 @@ begin fkKeyword: Result := JDefRef('Key'); fkLambda: Result := JDefRef('Fn'); fkIdentifier: Result := JDefRef('Id'); + fkString: Result := JType('string'); - fkValue: Result := JAnyOf([JType('number'), JType('string'), JType('boolean')]); - fkNullableNode: Result := JAnyOf([JDefRef('Node'), JType('null')]); - fkArrayOfNodes: Result := JArray(JDefRef('Node')); + + fkValue: + begin + // number | string | boolean + Result := JAnyOf([JType('number'), JType('string'), JType('boolean')]); + end; + + fkNullableNode: + begin + // Node | null + // Note: OpenAPI/Gemini often prefers 'nullable: true' over explicit null type, + // but anyOf [Node, {type: null}] works in full JSON schema. + Result := JAnyOf([JDefRef('Node'), JType('null')]); + end; + + fkArrayOfNodes: + begin + // standard array of Nodes + Result := JType('array'); + Result.AddPair('items', JDefRef('Node')); + end; fkArrayOfPairs: begin // [[Cond, Branch], ...] - pair := JFixedArray([JDefRef('Node'), JDefRef('Node')]); - Result := JArray(pair); + pair := JTupleArray([JDefRef('Node'), JDefRef('Node')]); + Result := JType('array'); + Result.AddPair('items', pair); end; fkPipeInputs: begin // 1. Selector Tuple: ["Tuple", [Key, ...]] - selTuple := JTuple(JArray(JDefRef('Key'))); + // Note: This is an array of Keys inside the Tuple structure + var keyArray := JType('array'); + keyArray.AddPair('items', JDefRef('Key')); + selTuple := JTuple(keyArray); // 2. Entry Node: ["Tuple", [Id, SelectorTuple]] - entryNode := JTuple(JFixedArray([JDefRef('Id'), selTuple])); + entryNode := JTupleArray([JDefRef('Id'), selTuple]); - // 3. Outer Inputs List: ["Tuple", [EntryNode, ...]] - Result := JTuple(JArray(entryNode)); + // 3. Wrap in ["Tuple", [Array of EntryNodes]] + var entriesArray := JType('array'); + entriesArray.AddPair('items', entryNode); + + Result := JTuple(entriesArray); end; else Result := TJSONObject.Create; @@ -142,7 +170,11 @@ var anyOfNodes: TJSONArray; tag: string; fields: TList; - minCount, maxCount: Integer; + itemsArr: TJSONArray; + + // Properties Wrapper + props, programProp: TJSONObject; + reqArr: TJSONArray; begin ctx := TRttiContext.Create; Result := TJSONObject.Create; @@ -150,7 +182,7 @@ begin anyOfNodes := TJSONArray.Create; try - // 1. Scan all interfaces for AstTag metadata + // 1. Scan all interfaces for typ in ctx.GetTypes do begin if typ.TypeKind <> tkInterface then @@ -170,39 +202,41 @@ begin if tag = '' then continue; - // Sort fields by index + // Sort fields fields.Sort( TComparer .Construct(function(const L, R: AstFieldAttribute): Integer begin Result := L.Index - R.Index; end) ); + // Construct Tuple Definition for this Tag: ["Tag", Arg1, Arg2] nodeDef := TJSONObject.Create; nodeDef.AddPair('type', 'array'); - var items := TJSONArray.Create; - items.AddElement(TJSONObject.Create.AddPair('const', tag)); + itemsArr := TJSONArray.Create; - minCount := 1; - maxCount := 1; + // Item 0: The Tag (as Const/Enum) + var tagConst := TJSONObject.Create; + tagConst.AddPair('type', 'string'); + var enumArr := TJSONArray.Create; + enumArr.Add(tag); + tagConst.AddPair('enum', enumArr); + itemsArr.AddElement(tagConst); + // Items 1..N: The Fields for var f in fields do begin - inc(maxCount); - // Only non-nullable fields increase minCount - if f.Kind <> fkNullableNode then - minCount := maxCount; - - items.AddElement(GetJsonType(f.Kind)); + itemsArr.AddElement(GetJsonType(f.Kind)); end; - nodeDef.AddPair('prefixItems', items); - nodeDef.AddPair('minItems', TJSONNumber.Create(minCount)); - nodeDef.AddPair('maxItems', TJSONNumber.Create(maxCount)); - // additionalItems removed as maxItems guarantees strictness + nodeDef.AddPair('items', itemsArr); + nodeDef.AddPair('minItems', TJSONNumber.Create(itemsArr.Count)); + nodeDef.AddPair('maxItems', TJSONNumber.Create(itemsArr.Count)); + nodeDef.AddPair('additionalItems', TJSONBool.Create(False)); + // Add to defs defs.AddPair(tag, nodeDef); - // Add reference to the global AstNode union + // Add to main Node Union astNodeRef := TJSONObject.Create; astNodeRef.AddPair('$ref', '#/$defs/' + tag); anyOfNodes.AddElement(astNodeRef); @@ -212,13 +246,29 @@ begin end; end; - // 2. Define the base AstNode anyOf union + // 2. Define generic "Node" union var astNodeBase := TJSONObject.Create; + + // WICHTIG: Das fehlende "type": "array" hat den Fehler verursacht. + astNodeBase.AddPair('type', 'array'); + astNodeBase.AddPair('anyOf', anyOfNodes); defs.AddPair('Node', astNodeBase); - // 3. Assemble final Root Object - Result.AddPair('$ref', '#/$defs/Node'); + // 3. ROOT OBJECT WRAPPER + Result.AddPair('type', 'object'); + + props := TJSONObject.Create; + programProp := TJSONObject.Create; + programProp.AddPair('$ref', '#/$defs/Node'); + props.AddPair('program', programProp); + Result.AddPair('properties', props); + + reqArr := TJSONArray.Create; + reqArr.Add('program'); + Result.AddPair('required', reqArr); + + Result.AddPair('additionalProperties', TJSONBool.Create(False)); Result.AddPair('$defs', defs); finally