Created
May 11, 2025 17:03
-
-
Save hodzanassredin/15461c63ce08f33a705bc7fc972dacb1 to your computer and use it in GitHub Desktop.
llm client in black box builder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MODULE LlmOpenAI; | |
IMPORT | |
TFLog := AosTFLog, | |
AosStreams, | |
AosStrings, | |
CryptoBase64, | |
Dialog, | |
Files, | |
HttpClient, | |
HttpParser, | |
HttpTools, | |
Meta, | |
StdLog, | |
Strings, | |
W3cDStrings, | |
W3cJSON, | |
W3cObjects, | |
W3cStreams; | |
VAR | |
log: TFLog.Log; | |
CONST | |
Ok* = HttpParser.OK; | |
BadAuth* = HttpParser.Forbidden; | |
TYPE | |
Message* = POINTER TO RECORD | |
role*, content*: POINTER TO ARRAY OF CHAR; | |
END; | |
ChatLog* = POINTER TO RECORD | |
msg*: Message; | |
next*: ChatLog; | |
END; | |
CompletionParams* = POINTER TO RECORD | |
model*: ARRAY 32 OF CHAR; | |
temperature*, top_p*: REAL; | |
n*, max_tokens*: INTEGER; | |
END; | |
Client* = POINTER TO RECORD | |
url: ARRAY 256 OF CHAR; (* Base URL *) | |
token: ARRAY 128 OF SHORTCHAR; (* API token *) | |
client: HttpClient.HTTPConnection; | |
END; | |
W3cStreamReader = POINTER TO RECORD(W3cStreams.Reader) | |
r: AosStreams.Reader; | |
END; | |
W3cStreamWriter = POINTER TO RECORD(W3cStreams.Writer) | |
w: AosStreams.Writer; | |
END; | |
PROCEDURE (this: W3cStreamReader) Get (): CHAR; | |
VAR ch16: INTEGER; | |
BEGIN | |
IF (this.r.res = AosStreams.Ok) & (this.r.UTF8Char(ch16)) THEN | |
RETURN CHR(ch16); | |
ELSE | |
this.ok := FALSE; | |
RETURN 0X | |
END | |
END Get; | |
PROCEDURE (this: W3cStreamWriter) Char* (ch: CHAR), EXTENSIBLE; | |
BEGIN | |
this.ok := this.w.UTF8Char(ORD(ch)); | |
END Char; | |
PROCEDURE NewClient* (token, url: ARRAY OF CHAR): Client; | |
VAR c: Client; | |
BEGIN | |
NEW(c); | |
c.token := SHORT(token); | |
c.url := SHORT(url); | |
RETURN c; | |
END NewClient; | |
PROCEDURE (c: Client) Close*, NEW; | |
BEGIN | |
c.client.Close(); | |
END Close; | |
PROCEDURE NewChatLog* (): ChatLog; | |
VAR chatLog: ChatLog; | |
BEGIN | |
NEW(chatLog); | |
chatLog.msg := NIL; | |
RETURN chatLog; | |
END NewChatLog; | |
PROCEDURE (log: ChatLog) AddMessage* (role, content: ARRAY OF CHAR), NEW; | |
VAR msg: Message; curr: ChatLog; | |
BEGIN | |
NEW(msg); | |
NEW(msg.role, LEN(role$)); | |
msg.role^ := role$; | |
NEW(msg.content, LEN(content$)); | |
msg.content^ := content$; | |
curr := log; | |
WHILE curr.next # NIL DO | |
curr := curr.next; | |
END; | |
curr.msg := msg; | |
END AddMessage; | |
PROCEDURE NewJSONString (str: ARRAY OF CHAR): W3cJSON.String; | |
VAR | |
res: W3cJSON.String; | |
BEGIN | |
NEW(res); res.val := W3cDStrings.NewString(str$); | |
RETURN res; | |
END NewJSONString; | |
PROCEDURE NewJSONNumberFromReal (num: REAL): W3cJSON.Number; | |
VAR | |
res: W3cJSON.Number; | |
BEGIN | |
NEW(res); | |
res.isInt := FALSE; | |
res.realVal := num; | |
RETURN res; | |
END NewJSONNumberFromReal; | |
PROCEDURE NewJSONNumberFromInt (num: INTEGER): W3cJSON.Number; | |
VAR | |
res: W3cJSON.Number; | |
BEGIN | |
NEW(res); | |
res.isInt := TRUE; | |
res.intVal := num; | |
RETURN res; | |
END NewJSONNumberFromInt; | |
PROCEDURE ToJSONMessage (msg: Message): W3cJSON.Object; | |
VAR obj: W3cJSON.Object; str: W3cJSON.String; | |
BEGIN | |
NEW(obj); obj.entries := W3cObjects.NewArrayDict(); | |
obj.entries.Add("role", NewJSONString(msg.role)); | |
obj.entries.Add("content", NewJSONString(msg.content)); | |
RETURN obj; | |
END ToJSONMessage; | |
PROCEDURE (log: ChatLog) WriteTo (params: CompletionParams; stream: AosStreams.Writer), NEW; | |
VAR | |
cur: ChatLog; | |
req: W3cJSON.Object; | |
arr: W3cJSON.Array; | |
i: INTEGER; | |
writer: W3cStreamWriter; | |
BEGIN | |
NEW(req); req.entries := W3cObjects.NewArrayDict(); | |
NEW(arr); arr.elems := W3cObjects.NewList(); | |
cur := log; | |
WHILE cur # NIL DO | |
arr.elems.Add(ToJSONMessage(cur.msg)); | |
cur := cur.next; | |
END; | |
req.entries.Add("messages", arr); | |
req.entries.Add("model", NewJSONString(params.model)); | |
req.entries.Add("temperature", NewJSONNumberFromReal(params.temperature)); | |
req.entries.Add("max_tokens", NewJSONNumberFromInt(params.max_tokens)); | |
req.entries.Add("top_p", NewJSONNumberFromReal(params.top_p)); | |
req.entries.Add("n", NewJSONNumberFromInt(params.n)); | |
NEW(writer); | |
writer.w := stream; | |
W3cJSON.Write(req, writer, ""); | |
END WriteTo; | |
PROCEDURE (c: Client) ChatCompletion* (params: CompletionParams; chatLog: ChatLog; OUT res: INTEGER): AosStreams.Reader, NEW; | |
VAR | |
payload, response: AosStreams.Reader; | |
buf: AosStreams.Buffer; | |
w: AosStreams.Writer; | |
authHeader: ARRAY 256 OF SHORTCHAR; | |
BEGIN | |
NEW(c.client); c.client.New(); | |
(* Установка заголовков *) | |
c.client.requestHeader.useragent := "BlackBox/0.1"; | |
HttpParser.SetAdditionalFieldValue(c.client.requestHeader.additionalFields, "Content-Type", "application/json;charset=UTF-8"); | |
(* Авторизация через Bearer Token *) | |
authHeader := "Bearer " + c.token$; | |
HttpParser.SetAdditionalFieldValue(c.client.requestHeader.additionalFields, "Authorization", authHeader); | |
(* Запись в поток *) | |
NEW(buf); buf.Init(1024); | |
w := buf.GetWriter(); | |
(* Формирование JSON тела запроса *) | |
chatLog.WriteTo(params, w); | |
w.Update(); | |
payload := buf.GetReader(); | |
(* Выполнение POST-запроса *) | |
c.client.Post(SHORT(c.url), "", "application/json", payload, buf.GetLength(), response, res); | |
res := c.client.responseHeader.statuscode; | |
RETURN response; | |
END ChatCompletion; | |
PROCEDURE JsonObjToRecord (obj: W3cJSON.Object; rec: ANYPTR); | |
VAR | |
it: Meta.Item; sc: Meta.Scanner; nm: Meta.Name; | |
isOk: BOOLEAN; tmpInt: INTEGER; tmpStr: POINTER TO ARRAY OF CHAR; | |
res: INTEGER; | |
any: ANYPTR; | |
BEGIN | |
Meta.GetItem(rec, it); | |
sc.ConnectTo(it); | |
sc.Scan; | |
ASSERT(~sc.eos); | |
WHILE ~sc.eos DO | |
sc.GetObjName(nm); | |
(*Log.String(nm); Log.Int(sc.this.obj); Log.Int(sc.this.typ); Log.Ln;*) | |
CASE sc.this.typ OF | |
| Meta.arrTyp: | |
tmpStr := obj.entries.Get(nm)(W3cJSON.String).val; | |
sc.this.PutStringVal(tmpStr, isOk); | |
| Meta.intTyp: | |
tmpInt := SHORT(obj.entries.Get(nm)(W3cJSON.Number).intVal); | |
sc.this.PutIntVal(tmpInt); | |
END; | |
sc.Scan; | |
END; | |
END JsonObjToRecord; | |
(* Парсинг ответа от OpenAI *) | |
PROCEDURE ParseResponse (response: AosStreams.Reader): Message; | |
VAR | |
jsonParser: W3cJSON.Parser; | |
val: W3cJSON.Value; | |
obj, choiceObj, messageObj: W3cJSON.Object; | |
arr: W3cJSON.Array; | |
enum: W3cObjects.Enumerator; | |
msg: Message; | |
in: W3cStreamReader; err: W3cJSON.ErrorHandler; | |
BEGIN | |
NEW(in); in.r := response; | |
NEW(jsonParser); jsonParser.Init(in, err); | |
jsonParser.SetStringPooling(W3cJSON.DefaultStringPooling); | |
val := jsonParser.Parse(); | |
obj := val(W3cJSON.Object); | |
(* Получаем первый элемент из массива choices *) | |
arr := obj.entries.Get("choices")(W3cJSON.Array); | |
enum := arr.elems.GetEnumerator(); | |
IF enum.HasMoreElements() THEN | |
choiceObj := enum.GetNext()(W3cJSON.Object); | |
messageObj := choiceObj.entries.Get("message")(W3cJSON.Object); | |
NEW(msg); | |
JsonObjToRecord(messageObj, msg); | |
ELSE | |
msg := NIL; | |
END; | |
RETURN msg; | |
END ParseResponse; | |
BEGIN | |
NEW(log); log.Init("OpenAI Client"); | |
log.SetLogToOut(TRUE); | |
CLOSE | |
log.Close(); | |
END LlmOpenAI. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment