267 lines
6.6 KiB
ObjectPascal
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.
|