Due to potential privacy concerns with DeepSeek servers (we’re unsure if the data sent over the paid API is kept private), I looked into some smaller DeepSeek models available on the Ollama.com site. These models use less complex AI with fewer parameters than the online version, but they might still be good enough for an average Delphi programmer. We’ll see.
For testing, I used a powerful RTX 4090 card with 24 GB of memory. If your graphics card has less memory, your selection of useful models will be more limited.
I asked all models the same two questions: one on a general programming topic and another specific to the FireMonkey platform. The first question was:
"I have a multiline string containing newline ASCII characters (TMemo.Text). I want to change it to a single-line string with only printable ASCII characters. I could do that with BASE64 encoding, for example. I would, however, like to keep the text as much readable as possible by "encoding" only non-printable characters. Is there a simple way to do that?"
You can check Codellama’s response in an older post (Codellama being the only local model I had tested so far): Delphi and AI [5]: Encoding Multi-line Strings.
Last week, I asked the same question to the online DeepSeek-Reasoning model. Check the answers in this post: Delphi and AI [6]: DeepSeek-Reasoning Model.
The second question was:
"How can I copy text to clipboard in a Delphi Firemonkey application?"
Read here for Codellama’s response: Delphi and AI [4]: Device-ndependent clipboard.
The answer from the online DeepSeek-Reasoning model can be found at the end of this post.
Let’s see how the models are performing! As always, full logs are available on GitHub.
[deepseek-coder-v2]
DeepSeek-coder-v2 is the newest coding-specific refinement of the DeepSeek model. It was updated 4 months ago and offers two versions: with 16b and 236b parameters. As I have no chance of running the latter locally, I tested the 16 billing parameter version. It uses roughly 13 GB of graphics memory.
The first aswer was:
function EncodeNonPrintable(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
begin
case Ord(S[I]) of
0..31, 127: // Non-printable ASCII characters (including control codes and DEL)
Result := Result + Format('\x%2.2X', [Ord(S[I])]);
else
Result := Result + S[I];
end;
end;
end;
As we've seen before, this is a classical AI approach that almost works. The only problem is that the character '\' is not encoded. Easy to fix, but it is on you to find the problem and implement the fix.
uses
System.SysUtils, FMX.Clipboard, FMX.Forms;
procedure TForm1.CopyToClipboardButtonClick(Sender: TObject);
var
ClipboardService: IFMXClipboardService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipboardService)) then
begin
ClipboardService.SetText('Your text to copy');
ShowMessage('Text copied to clipboard!');
end
else
begin
ShowMessage('Clipboard service not available on this platform.');
end;
end;
[deepseek-coder:33b]
I had also tested an older deepseek-coder model. As it offers a 33b version (in addition to 1.3b and 6.7b) which just fits onto my graphics card (it used roughly 22 GB of memory) I wanted to know if how it would compare with a newer but smaller model.
Solution of the "encoding" question is interesting as it preallocates a larger string and truncates it at the end:
function EncodeNonPrintables(const InputString: String): String;
var
i, j: Integer;
begin
SetLength(Result, Length(InputString) * 2); // Worst case scenario
j := 0;
for i := 1 to Length(InputString) do begin
if (Ord(InputString[i]) < 32) or (Ord(InputString[i]) > 126) then begin
// Non-printable character, replace it with '\x' followed by its hex value.
Result[j] := Format('\x%2.2X', [Ord(InputString[i])]);
Inc(j, 4);
end else begin
// Printable character, copy as is.
Result[j] := InputString[i];
Inc(j);
end;
end;SetLength(Result, j); // Trim the result string to its actual length.
end;
Let's see how it handles copying to the clipboard:
uses FMX.Platform;
procedure CopyToClipboard(const Text: string);
var
ClipboardService: IFMXClipboardService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipboardService)) then
ClipboardService.SetClipboard(Text);
end;
Nice!
[deepseek-r1-14b]
I've also tested two variations of the deepseek-reasoning model, last update 11 days ago. From the long list of available versions (from 1.5b to 671b parameters) I picked 14b and 32b variations.
function ConvertToSingleLine(const S: string): string;
var
i: Integer;
begin
Result := '';
for i := 0 to Length(S) - 1 do
begin
case S[i] of
#10, #13:
// Replace newlines with spaces
Result += ' ';
#9, #11, #12: // Other control characters like tab (9), vertical tab (11), form feed (12)
Result += '?';
else
if S[i] < #32 or S[i] > #126 then
// Replace other non-printable ASCII characters with '?'
Result += '?'
else
// Keep printable characters as they are
Result += S[i];
end;
end;
end;
Dear AI, I don't know how to tell you, but that is not a Delphi code.
What about the Firemonkey and clipboard?
uses
FMX.Clipboard;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Access the clipboard through the Application's Clipboard property
TPlatform.Current.Clipboard.SetText('Your desired text here');
end;
[deepseek-r1-32b]
What about the larger brother with 32b parameters? This one used almost full 24 GB of graphics memory. Did this improve the model?
Not really. The first answer is again not written in Delphi (plus it doesn't encode the '\' character):
function MakeSingleLine(const S: string): string;
var
i: Integer;
c: Char;
begin
Result := '';
for i := 1 to Length(S) do
begin
c := S[i];
if (c >= #32) and (c <= #126) then
Result += c
else
Result += Format('\x%0.2x', [Ord(c)]);
end;
end;
The second answer also fails completely:
uses
System.Clipboard;
procedure CopyTextToClipboard(const AText: string);
begin
try
TClipboard.SetText(AText);
except
// Handle any exceptions (e.g., platform-specific errors)
on E: Exception do
ShowMessage('Error copying text to clipboard: ' + E.Message);
end;
end;
[deepseek-reasoning]
We've submitted the first question to the online deepseek-reasoning before and it performed admirably. But how does it handle the Firemonkey clipboard question?
Not very well:
uses
FMX.Clipboard; // Include the clipboard unit
procedure TForm1.CopyTextToClipboard(const AText: string);
begin
if Assigned(TClipboard.Default) then
TClipboard.Default.SetText(AText);
end;
var
ClipboardService: IFMXClipboardService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, ClipboardService) then
ClipboardService.SetClipboard('My text');
end;
The AI has started thinking in the correct direction but then tried to simplify the answer and arrived at the incorrect conclusion.
Recap
The most useful local model - for Delphi programmers - seems to be deepseek-coder-v2. It works reasonably well and uses "only" 13 GB of a graphics memory. Because of that I'll from now on include it in my AI testing.
I've tried mistral:7b-instruct-v0.3-fp16 from Ollama big list of Mistral models, for Delphi it seems quite good with Delphi. Here's its answer to your question:
ReplyDeleteYes, you can create a function that replaces non-printable ASCII characters with their corresponding hexadecimal
representations, while keeping newline characters intact. Here's an example in Delphi:
```delphi
function SafeEncode(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
if (S[I] < #32) or (S[I] > #126) then
Result := Result + '\\x' + IntToHex(Ord(S[I]), 2)
else
Result := Result + S[I];
end;
```
You can call this function on your TMemo.Text before saving it:
```delphi
TMemo1.Lines.SaveToFile('example.txt'); // Save original multiline text
TMemo1.Text := SafeEncode(TMemo1.Text); // Encode non-printable characters
TMemo1.Lines.SaveToFile('example_encoded.txt'); // Save encoded text
```
When you need to decode the text, you can create a corresponding function:
```delphi
function SafeDecode(const S: string): string;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
if S[I] = #34 then // Handle backslashes and quotes properly
if S[I + 1] = 'x' then
begin
Inc(I);
Result := Result + StrToCodePoint(IntHexStrToBin2(Copy(S, I, 6))) - #128;
Inc(I, 6);
end
else if (S[I] >= #32) and (S[I] <= #126) then
Result := Result + S[I];
end;
```
You can decode the text when you need to display it:
```delphi
TMemo1.Text := SafeDecode(FileReadAllText('example_encoded.txt')); // Decode encoded text and assign to
TMemo1.Text
```
These functions replace characters outside of the ASCII printable range (32-126) with their hexadecimal
representation, while keeping newline characters as is. Non-printable ASCII characters will look like `\xHH`,
where HH represents the hexadecimal value of the character.