Gemini Test
This commit is contained in:
@@ -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}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
+62
-55
@@ -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(
|
||||
@@ -162,20 +177,31 @@ begin
|
||||
TThread.Queue(
|
||||
nil,
|
||||
procedure
|
||||
begin
|
||||
// Statuswechsel oder allererster Chunk -> Neuer Block
|
||||
if FirstChunk or (IsThought <> LastWasThought) then
|
||||
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 + '] ';
|
||||
FMarkdown.BeginBlock(btMarkdown, 'Thinking...');
|
||||
// Optional: Kursiver Block für Gedanken
|
||||
// Wir hängen Gedanken oft als Zitat oder kursiv an
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Normale Antwort
|
||||
ChatMemo.Text := ChatMemo.Text + TextChunk;
|
||||
// Wenn wir aus einem Gedanken kommen oder starten
|
||||
FMarkdown.BeginBlock(btMarkdown, 'Gemini');
|
||||
end;
|
||||
|
||||
ChatMemo.GoToTextEnd;
|
||||
LastWasThought := IsThought;
|
||||
FirstChunk := False;
|
||||
end;
|
||||
|
||||
// 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,6 +229,7 @@ end;
|
||||
|
||||
procedure TForm1.Edit1Exit(Sender: TObject);
|
||||
begin
|
||||
if ModelsComboBox.ItemIndex < 0 then
|
||||
GetAvailableModels;
|
||||
end;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user