PCITY_DID/SOURCE/LLM_Mistral/LLM_Mistral_FMX/uMain.pas
2026-09-04 15:12:16 +09:00

267 lines
6.6 KiB
ObjectPascal

unit uMain;
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.StdCtrls, FMX.Controls.Presentation,
FMX.Layouts, REST.Types, REST.Client, Data.Bind.Components, Data.Bind.ObjectScope,
System.JSON, FMX.Edit;
type
TForm1 = class(TForm)
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
RESTResponse1: TRESTResponse;
MaterialOxfordBlueSB: TStyleBook;
Timer1: TTimer;
MemoOutput: TMemo;
MemoResponse: TMemo;
Panel1: TPanel;
BtnSend: TButton;
MemoInput: TMemo;
Panel2: TPanel;
Edit1: TEdit;
Label1: TLabel;
Splitter1: TSplitter;
procedure BtnSendClick(Sender: TObject);
procedure MemoInputKeyDown(Sender: TObject; var Key: Word;
var KeyChar: WideChar; Shift: TShiftState);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
procedure HandleResponse;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.BtnSendClick(Sender: TObject);
var
JsonObj: TJSONObject;
memostr : string;
begin
memostr := MemoInput.Text;
MemoInput.Text := '';
MemoInput.Lines.Add('');
// 1. 입력 확인
if Trim(memostr) = '' then Exit;
// 2. UI 업데이트 (모바일에서는 키패드 내려주기 위해 포커스 이동 등을 고려할 수 있음)
MemoOutput.Lines.Add('질문: ' + memostr);
MemoOutput.Lines.Add('답변을 기다리는 중...');
MemoOutput.GoToTextEnd;
BtnSend.Enabled := False; // 중복 전송 방지
RESTClient1.BaseURL := Edit1.Text;
RESTRequest1.Accept := 'application/json, text/plain;q=0.9, text/html;q=0.8';
RESTRequest1.Method := rmPOST;
RESTRequest1.Resource := 'chat';
// 3. JSON 생성
JsonObj := TJSONObject.Create;
try
JsonObj.AddPair('prompt', memostr);
JsonObj.AddPair('max_tokens', TJSONNumber.Create(512)); // 토큰 수 넉넉하게
// 4. Request 설정
RESTRequest1.ClearBody;
// 중요: 모바일 문자셋 호환을 위해 TRESTContentType.ctAPPLICATION_JSON 명시
RESTRequest1.AddBody(JsonObj.ToString, TRESTContentType.ctAPPLICATION_JSON);
finally
JsonObj.Free;
end;
RESTRequest1.ExecuteAsync(
procedure
begin
// MemoInput.Lines.Text := RESTResponse1.Content
HandleResponse;
BtnSend.Enabled := True;
end, True, True,
procedure(AObject: TObject)
begin
MemoOutput.Lines.Add('Error: ' + ERESTException(AObject).Message);
BtnSend.Enabled := True;
end);
{
// 5. 비동기 전송 (ExecuteAsync)
// [수정] 다시 파라미터가 없는 기본 형태로 돌아갑니다.
RESTRequest1.ExecuteAsync(
procedure
begin
// 전송 성공 시 실행될 코드
HandleResponse;
BtnSend.Enabled := True;
end,
True, // Synchronized
True, // FreeThread
procedure(E: Exception)
begin
// 에러 발생 시 실행될 코드
ShowMessage('통신 에러: ' + E.Message);
BtnSend.Enabled := True;
end
);
}
{
// [비상 대책] 가장 단순한 형태 (에러 처리는 나중에 추가)
RESTRequest1.ExecuteAsync(
procedure
begin
HandleResponse;
BtnSend.Enabled := True;
end
);
}
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{$IF defined(MSWINDOWS)}
MemoInput.ImeMode := TImeMode.imSHanguel;
{$ELSEIF defined(MACOS)}
MemoInput.TextSettings.Font.Family := 'Apple SD Gothic Neo';
MemoInput.Lines.Add('');
{$ELSEIF defined(ANDROID)}
//
{$ELSEIF defined(IOS)}
//
{$ENDIF}
MemoInput.SetFocus;
end;
procedure TForm1.HandleResponse;
var
JsonValue: TJSONValue;
JsonObject: TJSONObject;
DataArray: TJSONArray;
DataItem: TJSONValue;
Status, MessageStr: string;
I: Integer;
Pair: TJSONPair;
begin
if RESTResponse1.StatusCode = 200 then
begin
JsonValue := RESTResponse1.JSONValue;
Timer1.Enabled := False;
MemoResponse.Text := 'Response: ' + JsonValue.ToString;
if JsonValue is TJSONObject then
begin
JsonObject := JsonValue as TJSONObject;
// 1. 서버 처리 상태(status) 확인
if JsonObject.TryGetValue<string>('status', Status) then
begin
// 2. 서버가 보낸 메시지(message) 추출
JsonObject.TryGetValue<string>('message', MessageStr);
MemoOutput.Lines.Add('상태: ' + Status);
MemoOutput.Lines.Add('메시지: ' + MessageStr);
// 3. 성공 시 실제 데이터(data) 파싱
if (Status = 'success') and JsonObject.TryGetValue<TJSONArray>('data', DataArray) then
begin
MemoOutput.Lines.Add('[데이터 내역]');
for I := 0 to DataArray.Count - 1 do
begin
DataItem := DataArray.Items[I];
if DataItem is TJSONObject then
begin
// 각 레코드의 키(컬럼명)-값(데이터)을 추출하여 출력
for Pair in (DataItem as TJSONObject) do
begin
MemoOutput.Lines.Add(' ' + Pair.JsonString.Value + ': ' + Pair.JsonValue.Value);
end;
MemoOutput.Lines.Add(' ------------------');
end;
end;
end
// 4. 데이터가 없는 경우(empty) 처리
else if Status = 'empty' then
begin
MemoOutput.Lines.Add('검색 결과가 존재하지 않습니다.');
end;
MemoOutput.Lines.Add('------------------------');
MemoOutput.GoToTextEnd;
end
else
begin
MemoOutput.Lines.Add('JSON 응답 구조가 기존과 다릅니다.');
end;
end;
end
else
begin
Timer1.Enabled := False;
ShowMessage('서버 오류 (' + RESTResponse1.StatusCode.ToString + '): ' + RESTResponse1.StatusText);
end;
end;
procedure TForm1.MemoInputKeyDown(Sender: TObject; var Key: Word;
var KeyChar: WideChar; Shift: TShiftState);
var
memostr : string;
begin
// 1. 엔터키가 아니면 무조건 탈출 (한글 조합 오류 방지)
if Key <> vkReturn then Exit;
// 2. Shift + Enter는 줄바꿈 허용
if ssShift in Shift then Exit;
memostr := MemoInput.Text;
// 3. 내용이 있을 때만 전송
if Trim(memostr) <> '' then
begin
// [중요] 순서가 핵심입니다!
// (1) 먼저 키 입력을 '무효화' 시켜서 더 이상 엔터가 먹지 않게 함
Key := 0;
KeyChar := #0;
MemoResponse.Text := '';
Timer1.Enabled := True;
// (2) 그 다음에 전송하고 메모장을 비움
BtnSendClick(nil);
// (3) 메모장이 비워진 후, 포커스나 커서 위치 재조정 (선택 사항)
// MemoInput.SelStart := 0;
end
else
begin
// 내용이 없어도 엔터키 입력 자체는 막아야 줄바꿈이 안 생김
Timer1.Enabled := False;
MemoInput.Text;
MemoInput.Lines.Add('');
Key := 0;
KeyChar := #0;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
MemoResponse.Text := MemoResponse.Text + '. ';
end;
end.