Gemini Test

This commit is contained in:
Michael Schimmel
2026-01-08 17:50:11 +01:00
parent a62ba89bca
commit 0efce47972
4 changed files with 85 additions and 75 deletions
+2 -1
View File
@@ -5,7 +5,8 @@ uses
FMX.Forms,
MainUnit in 'MainUnit.pas' {Form1},
Myc.Api.Gemini in 'Myc.Api.Gemini.pas',
Myc.System.Quota in 'Myc.System.Quota.pas';
Myc.System.Quota in 'Myc.System.Quota.pas',
Myc.Api.MarkdownStream in 'Myc.Api.MarkdownStream.pas';
{$R *.res}
+7 -8
View File
@@ -132,6 +132,7 @@
</DCCReference>
<DCCReference Include="Myc.Api.Gemini.pas"/>
<DCCReference Include="Myc.System.Quota.pas"/>
<DCCReference Include="Myc.Api.MarkdownStream.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
@@ -175,6 +176,12 @@
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Win32\Debug\GeminiTest.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>GeminiTest.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Win64\Debug\GeminiTest.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win64">
<RemoteName>GeminiTest.exe</RemoteName>
@@ -1139,14 +1146,6 @@
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Android">False</Platform>
<Platform value="Android64">False</Platform>
<Platform value="iOSDevice32">False</Platform>
<Platform value="iOSDevice64">False</Platform>
<Platform value="iOSSimARM64">False</Platform>
<Platform value="OSX32">False</Platform>
<Platform value="OSX64">False</Platform>
<Platform value="OSXARM64">False</Platform>
<Platform value="Win32">False</Platform>
<Platform value="Win64">True</Platform>
</Platforms>
+9 -6
View File
@@ -11,6 +11,7 @@ object Form1: TForm1
DesignerMasterStyle = 0
object AskButton: TButton
Anchors = [akRight, akBottom]
Default = True
Position.X = 800.000000000000000000
Position.Y = 577.000000000000000000
TabOrder = 1
@@ -85,16 +86,14 @@ object Form1: TForm1
StyleLookup = ''
TabOrder = 0
Text = 'Chat'
object ChatMemo: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
DataDetectorTypes = []
ExplicitSize.cx = 69.000000000000000000
ExplicitSize.cy = 26.000000000000000000
object ChatBrowser: TWebBrowser
Align = Client
Size.Width = 906.000000000000000000
Size.Height = 535.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
Viewport.Width = 902.000000000000000000
Viewport.Height = 531.000000000000000000
WindowsEngine = EdgeOnly
end
end
object CacheTabItem: TTabItem
@@ -108,6 +107,8 @@ object Form1: TForm1
StyleLookup = ''
TabOrder = 0
Text = 'Cache'
ExplicitSize.cx = 53.000000000000000000
ExplicitSize.cy = 26.000000000000000000
object CacheMemo: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
DataDetectorTypes = []
@@ -131,6 +132,8 @@ object Form1: TForm1
StyleLookup = ''
TabOrder = 0
Text = 'SysInstr'
ExplicitSize.cx = 61.000000000000000000
ExplicitSize.cy = 26.000000000000000000
object SystemInstructionMemo: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
DataDetectorTypes = []
+67 -60
View File
@@ -20,8 +20,10 @@ uses
FMX.StdCtrls,
FMX.Edit,
FMX.ListBox,
FMX.TabControl,
FMX.WebBrowser,
Myc.Api.Gemini,
FMX.TabControl;
Myc.Api.MarkdownStream;
type
TForm1 = class(TForm)
@@ -32,15 +34,15 @@ type
AskMemo: TMemo;
ResetChatButton: TButton;
TabControl1: TTabControl;
ChatTabItem: TTabItem;
CacheTabItem: TTabItem;
SysInstrTabItem: TTabItem;
ChatMemo: TMemo;
CacheMemo: TMemo;
SystemInstructionMemo: TMemo;
UploadCacheButton: TButton;
ThinkingCheckBox: TCheckBox;
GlobalQuoteLabel: TLabel;
ChatTabItem: TTabItem;
ChatBrowser: TWebBrowser;
procedure FormCreate(Sender: TObject);
procedure AskButtonClick(Sender: TObject);
procedure Edit1Exit(Sender: TObject);
@@ -48,8 +50,8 @@ type
procedure UploadCacheButtonClick(Sender: TObject);
private
FChat: IGeminiChat;
// Holds the client configured with the active cache
FCachedClient: IGeminiClient;
FMarkdown: IMarkdownStream;
procedure InitChat;
procedure UpdateQuotaDisplay;
public
@@ -63,37 +65,44 @@ implementation
uses
System.Threading,
Myc.System.Quota; // Einbindung für den globalen Zähler
Myc.System.Quota;
{$R *.fmx}
const
FallbackModel = 'gemini-3-flash-preview';
FallbackModel = 'gemini-2.5-flash-lite';
procedure TForm1.FormCreate(Sender: TObject);
begin
// 1. Markdown Stream initialisieren und mit Browser verbinden
FMarkdown := TMarkdownStream.Create;
FMarkdown.InitializeBrowser(ChatBrowser);
// 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
// Browser komplett leeren
if Assigned(FMarkdown) then
FMarkdown.Clear;
FMarkdown.BeginBlock(btMarkdown);
if Assigned(FCachedClient) then
begin
FChat := FCachedClient.StartChat;
ChatMemo.Lines.Add('--- New Chat Session (Cached Content Active) ---');
FMarkdown.Append('*--- New Chat Session ---* (Cached Content Active)');
end
else
begin
FChat := nil; // Lazy init in AskButton
ChatMemo.Lines.Add('--- New Chat Session ---');
FMarkdown.Append('*--- New Chat Session ---*');
FChat := nil;
end;
end;
@@ -126,33 +135,39 @@ begin
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 := '';
// --- USER BLOCK ---
FMarkdown.BeginBlock(btRawText, 'Prompt');
// Hier nutzen wir RawText für den Prompt selbst, um Markdown-Injection des Users zu verhindern,
// oder wir bleiben im Markdown Block für schöneres Rendering (Code-Blöcke des Users).
// Da User Markdown oft erwarten:
FMarkdown.Append(prompt);
TTask.Run(
procedure
var
CurrentChat: IGeminiChat;
resultData: TGeminiResult;
// State Tracking für den Stream
LastWasThought: Boolean;
FirstChunk: Boolean;
begin
CurrentChat := FChat;
if CurrentChat = nil then
exit;
LastWasThought := False;
FirstChunk := True;
// --- STREAMING ---
resultData :=
CurrentChat.SendMessageStream(
@@ -163,19 +178,30 @@ begin
nil,
procedure
begin
if IsThought then
// Statuswechsel oder allererster Chunk -> Neuer Block
if FirstChunk or (IsThought <> LastWasThought) 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;
if IsThought then
begin
FMarkdown.BeginBlock(btMarkdown, 'Thinking...');
// Optional: Kursiver Block für Gedanken
// Wir hängen Gedanken oft als Zitat oder kursiv an
end
else
begin
// Wenn wir aus einem Gedanken kommen oder starten
FMarkdown.BeginBlock(btMarkdown, 'Gemini');
end;
LastWasThought := IsThought;
FirstChunk := False;
end;
ChatMemo.GoToTextEnd;
// Inhalt anhängen
if IsThought then
FMarkdown.Append(TextChunk) // Wird im selben Markdown-Block gerendert
else
FMarkdown.Append(TextChunk);
end
);
end
@@ -187,12 +213,13 @@ begin
procedure
begin
if resultData.Text.StartsWith('Error') then
ChatMemo.Lines.Add(resultData.Text);
begin
FMarkdown.BeginBlock(btMarkdown, 'Error');
FMarkdown.Append('> ' + resultData.Text);
end;
// Update Labels (Session & Global Registry)
QuotaLabel.Text := Format('Input: %d | Output: %d', [resultData.Usage.PromptTokens, resultData.Usage.CandidatesTokens]);
UpdateQuotaDisplay;
AskButton.Enabled := True;
end
);
@@ -202,7 +229,8 @@ end;
procedure TForm1.Edit1Exit(Sender: TObject);
begin
GetAvailableModels;
if ModelsComboBox.ItemIndex < 0 then
GetAvailableModels;
end;
procedure TForm1.GetAvailableModels;
@@ -213,8 +241,6 @@ begin
if apiKey.IsEmpty then
exit;
ChatMemo.Lines.Add('Fetching available models...');
TTask.Run(
procedure
var
@@ -232,27 +258,22 @@ begin
begin
if Length(modelList) = 0 then
begin
ChatMemo.Lines.Add('No models found.');
FMarkdown.Append(sLineBreak + '*System: No models found.*' + sLineBreak);
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;
@@ -271,24 +292,22 @@ var
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.');
FMarkdown.Append(sLineBreak + '**Error:** Cache content is empty.' + sLineBreak);
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)...');
// Hinweis im Browser anzeigen
FMarkdown.Append(sLineBreak + '*Creating Cache...*' + sLineBreak);
TTask.Run(
procedure
@@ -298,33 +317,22 @@ begin
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);
FMarkdown.Append('**Success: Cache created!**' + sLineBreak);
FMarkdown.Append('ID: `' + cacheName + '`' + sLineBreak);
InitChat;
UploadCacheButton.Enabled := True;
end
);
except
on E: Exception do
begin
@@ -333,8 +341,7 @@ begin
nil,
procedure
begin
ChatMemo.Lines.Add('Cache Upload Failed:');
ChatMemo.Lines.Add(errMsg);
FMarkdown.Append('**Cache Upload Failed:** ' + errMsg + sLineBreak);
UploadCacheButton.Enabled := True;
end
);