├── Icon.png ├── hIcons.dcr ├── Media ├── ics.psd ├── openai.bmp ├── openai.png ├── openai_chat.bmp ├── openai_chat.png ├── openai_func.bmp ├── openai_func.png ├── openai_hist.bmp └── openai_hist.png ├── .gitattributes ├── OpenAIPackage.res ├── openai+delphi.png ├── Samples ├── Chat │ ├── SimpleChat.res │ ├── SimpleChat.dpr │ ├── SimpleChat.gv │ ├── Chat.Main.fmx │ └── Chat.Main.pas ├── Sample │ ├── SampleGPT.res │ └── SampleGPT.dpr ├── QuickChat │ ├── QuickChat.res │ └── QuickChat.dpr └── OpenAIGroup.groupproj ├── OpenAI.Component.Reg.pas ├── OpenAI.Types.pas ├── OpenAI.Errors.pas ├── LICENSE ├── OpenAI.Utils.Base64.pas ├── .gitignore ├── OpenAI.Engines.pas ├── OpenAIPackage.dpk ├── OpenAI.Chat.Functions.Samples.pas ├── OpenAI.Component.ChatHistory.pas ├── OpenAI.GigaChat.pas ├── OpenAI.Utils.ObjectHolder.pas ├── OpenAI.Chat.Functions.pas ├── OpenAI.Models.pas ├── OpenAI.Edits.pas ├── OpenAI.Utils.ChatHistory.pas ├── OpenAI.Component.Functions.pas ├── OpenAI.Files.pas ├── OpenAI.Embeddings.pas ├── OpenAI.API.Params.pas ├── OpenAI.Utils.JSON.Cleaner.pas ├── OpenAI.Moderations.pas ├── README.md ├── OpenAI.Completions.pas ├── OpenAI.FineTunes.pas ├── OpenAI.Audio.pas ├── OpenAI.Component.Chat.pas └── OpenAI.API.pas /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Icon.png -------------------------------------------------------------------------------- /hIcons.dcr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/hIcons.dcr -------------------------------------------------------------------------------- /Media/ics.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/ics.psd -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Media/openai.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai.bmp -------------------------------------------------------------------------------- /Media/openai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai.png -------------------------------------------------------------------------------- /OpenAIPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/OpenAIPackage.res -------------------------------------------------------------------------------- /openai+delphi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/openai+delphi.png -------------------------------------------------------------------------------- /Media/openai_chat.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_chat.bmp -------------------------------------------------------------------------------- /Media/openai_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_chat.png -------------------------------------------------------------------------------- /Media/openai_func.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_func.bmp -------------------------------------------------------------------------------- /Media/openai_func.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_func.png -------------------------------------------------------------------------------- /Media/openai_hist.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_hist.bmp -------------------------------------------------------------------------------- /Media/openai_hist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Media/openai_hist.png -------------------------------------------------------------------------------- /Samples/Chat/SimpleChat.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Samples/Chat/SimpleChat.res -------------------------------------------------------------------------------- /Samples/Sample/SampleGPT.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Samples/Sample/SampleGPT.res -------------------------------------------------------------------------------- /Samples/QuickChat/QuickChat.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HemulGM/DelphiOpenAI/HEAD/Samples/QuickChat/QuickChat.res -------------------------------------------------------------------------------- /Samples/QuickChat/QuickChat.dpr: -------------------------------------------------------------------------------- 1 | uses OpenAI; 2 | 3 | begin 4 | repeat print('GPT: ' + chat('token', input('You: '))) until False; 5 | end. 6 | -------------------------------------------------------------------------------- /OpenAI.Component.Reg.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Component.Reg; 2 | 3 | interface 4 | 5 | uses 6 | OpenAI, OpenAI.Component.Chat, OpenAI.Component.Functions, OpenAI.Component.ChatHistory; 7 | 8 | procedure Register; 9 | 10 | implementation 11 | 12 | uses 13 | System.Classes; 14 | 15 | procedure Register; 16 | begin 17 | RegisterComponents('OpenAI', [TOpenAIClient]); 18 | RegisterComponents('OpenAI', [TOpenAIChat]); 19 | RegisterComponents('OpenAI', [TOpenAIChatFunctions]); 20 | RegisterComponents('OpenAI', [TOpenAIChatHistory]); 21 | end; 22 | 23 | end. 24 | 25 | -------------------------------------------------------------------------------- /OpenAI.Types.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Types; 2 | 3 | interface 4 | 5 | type 6 | TDeletionStatus = class 7 | private 8 | FDeleted: Boolean; 9 | FId: string; 10 | FObject: string; 11 | public 12 | property Deleted: Boolean read FDeleted write FDeleted; 13 | property Id: string read FId write FId; 14 | property &Object: string read FObject write FObject; 15 | end; 16 | 17 | TBase64Data = record 18 | ContentType: string; 19 | Data: string; 20 | function ToString: string; 21 | end; 22 | 23 | implementation 24 | 25 | { TBase64Data } 26 | 27 | function TBase64Data.ToString: string; 28 | begin 29 | Result := 'data:' + ContentType + ';base64,' + Data; 30 | end; 31 | 32 | end. 33 | 34 | -------------------------------------------------------------------------------- /OpenAI.Errors.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Errors; 2 | 3 | interface 4 | 5 | type 6 | TError = class 7 | private 8 | FMessage: string; 9 | FType: string; 10 | FParam: string; 11 | FCode: Int64; 12 | public 13 | property Message: string read FMessage write FMessage; 14 | property &Type: string read FType write FType; 15 | property Param: string read FParam write FParam; 16 | property Code: Int64 read FCode write FCode; 17 | end; 18 | 19 | TErrorResponse = class 20 | private 21 | FError: TError; 22 | public 23 | property Error: TError read FError write FError; 24 | destructor Destroy; override; 25 | end; 26 | 27 | implementation 28 | 29 | { TErrorResponse } 30 | 31 | destructor TErrorResponse.Destroy; 32 | begin 33 | if Assigned(FError) then 34 | FError.Free; 35 | inherited; 36 | end; 37 | 38 | end. 39 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 HemulGM 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Samples/Chat/SimpleChat.dpr: -------------------------------------------------------------------------------- 1 | program SimpleChat; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | Chat.Main in 'Chat.Main.pas' {FormChat}, 7 | OpenAI.API.Params in '..\..\OpenAI.API.Params.pas', 8 | OpenAI.API in '..\..\OpenAI.API.pas', 9 | OpenAI.Audio in '..\..\OpenAI.Audio.pas', 10 | OpenAI.Chat.Functions in '..\..\OpenAI.Chat.Functions.pas', 11 | OpenAI.Chat.Functions.Samples in '..\..\OpenAI.Chat.Functions.Samples.pas', 12 | OpenAI.Chat in '..\..\OpenAI.Chat.pas', 13 | OpenAI.Completions in '..\..\OpenAI.Completions.pas', 14 | OpenAI.Component.Chat in '..\..\OpenAI.Component.Chat.pas', 15 | OpenAI.Edits in '..\..\OpenAI.Edits.pas', 16 | OpenAI.Embeddings in '..\..\OpenAI.Embeddings.pas', 17 | OpenAI.Engines in '..\..\OpenAI.Engines.pas', 18 | OpenAI.Errors in '..\..\OpenAI.Errors.pas', 19 | OpenAI.Files in '..\..\OpenAI.Files.pas', 20 | OpenAI.FineTunes in '..\..\OpenAI.FineTunes.pas', 21 | OpenAI.FineTuning in '..\..\OpenAI.FineTuning.pas', 22 | OpenAI.Images in '..\..\OpenAI.Images.pas', 23 | OpenAI.Models in '..\..\OpenAI.Models.pas', 24 | OpenAI.Moderations in '..\..\OpenAI.Moderations.pas', 25 | OpenAI in '..\..\OpenAI.pas', 26 | OpenAI.Utils.ChatHistory in '..\..\OpenAI.Utils.ChatHistory.pas', 27 | OpenAI.Component.Functions in '..\..\OpenAI.Component.Functions.pas', 28 | OpenAI.Assistants in '..\..\OpenAI.Assistants.pas', 29 | OpenAI.Types in '..\..\OpenAI.Types.pas', 30 | OpenAI.Utils.ObjectHolder in '..\..\OpenAI.Utils.ObjectHolder.pas', 31 | OpenAI.Utils.Base64 in '..\..\OpenAI.Utils.Base64.pas', 32 | OpenAI.Utils.JSON.Cleaner in '..\..\OpenAI.Utils.JSON.Cleaner.pas', 33 | OpenAI.Component.ChatHistory in '..\..\OpenAI.Component.ChatHistory.pas'; 34 | 35 | {$R *.res} 36 | 37 | begin 38 | Application.Initialize; 39 | Application.CreateForm(TFormChat, FormChat); 40 | Application.Run; 41 | end. 42 | -------------------------------------------------------------------------------- /OpenAI.Utils.Base64.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Utils.Base64; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.NetEncoding, System.Classes, System.Net.Mime, 7 | OpenAI.Types; 8 | 9 | function FileToBase64(const FileName: TFileName): TBase64Data; 10 | 11 | function StreamToBase64(Stream: TStream; const ContentType: string): TBase64Data; 12 | 13 | function GetFileContentType(const FileName: TFileName): string; 14 | 15 | implementation 16 | 17 | function GetFileContentType(const FileName: TFileName): string; 18 | var 19 | LKind: TMimeTypes.TKind; 20 | begin 21 | TMimeTypes.Default.GetFileInfo(FileName, Result, LKind); 22 | end; 23 | 24 | function FileToBase64(const FileName: TFileName): TBase64Data; 25 | var 26 | FS: TFileStream; 27 | Base64: TStringStream; 28 | begin 29 | FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); 30 | try 31 | Base64 := TStringStream.Create('', TEncoding.UTF8); 32 | try 33 | {$IF RTLVersion >= 35.0} 34 | TNetEncoding.Base64String.Encode(FS, Base64); 35 | {$ELSE} 36 | TNetEncoding.Base64.Encode(FS, Base64); 37 | {$ENDIF} 38 | Result.Data := Base64.DataString; 39 | Result.ContentType := GetFileContentType(FileName); 40 | finally 41 | Base64.Free; 42 | end; 43 | finally 44 | FS.Free; 45 | end; 46 | end; 47 | 48 | function StreamToBase64(Stream: TStream; const ContentType: string): TBase64Data; 49 | var 50 | Base64: TStringStream; 51 | begin 52 | Base64 := TStringStream.Create('', TEncoding.UTF8); 53 | try 54 | {$IF RTLVersion >= 35.0} 55 | TNetEncoding.Base64String.Encode(Stream, Base64); 56 | {$ELSE} 57 | TNetEncoding.Base64.Encode(Stream, Base64); 58 | {$ENDIF} 59 | Result.Data := Base64.DataString; 60 | Result.ContentType := ContentType; 61 | finally 62 | Base64.Free; 63 | end; 64 | end; 65 | 66 | end. 67 | 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | /Samples/Chat/token.txt 71 | -------------------------------------------------------------------------------- /OpenAI.Engines.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Engines; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API; 7 | 8 | type 9 | TEngine = class 10 | private 11 | FId: string; 12 | FObject: string; 13 | FOwner: string; 14 | FReady: Boolean; 15 | public 16 | property Id: string read FId write FId; 17 | property &Object: string read FObject write FObject; 18 | property Owner: string read FOwner write FOwner; 19 | property Ready: Boolean read FReady write FReady; 20 | end; 21 | 22 | TEngines = class 23 | private 24 | FData: TArray; 25 | FObject: string; 26 | public 27 | property Data: TArray read FData write FData; 28 | property &Object: string read FObject write FObject; 29 | destructor Destroy; override; 30 | end; 31 | 32 | TEnginesRoute = class(TOpenAIAPIRoute) 33 | public 34 | /// 35 | /// Lists the currently available (non-finetuned) models, and provides basic information about each one such as the owner and availability. 36 | /// 37 | function List: TEngines; deprecated 'The Engines endpoints are deprecated. Please use their replacement, Models, instead.'; 38 | /// 39 | /// Retrieves a model instance, providing basic information about it such as the owner and availability. 40 | /// 41 | /// The ID of the engine to use for this request 42 | function Retrieve(const EngineId: string): TEngine; deprecated 'The Engines endpoints are deprecated. Please use their replacement, Models, instead.'; 43 | end; 44 | 45 | implementation 46 | 47 | { TEnginesRoute } 48 | 49 | function TEnginesRoute.List: TEngines; 50 | begin 51 | Result := API.Get('engines'); 52 | end; 53 | 54 | function TEnginesRoute.Retrieve(const EngineId: string): TEngine; 55 | begin 56 | Result := API.Get('engines' + '/' + EngineId); 57 | end; 58 | 59 | { TEngines } 60 | 61 | destructor TEngines.Destroy; 62 | var 63 | Item: TEngine; 64 | begin 65 | for Item in FData do 66 | Item.Free; 67 | inherited; 68 | end; 69 | 70 | end. 71 | 72 | -------------------------------------------------------------------------------- /OpenAIPackage.dpk: -------------------------------------------------------------------------------- 1 | package OpenAIPackage; 2 | 3 | {$R *.res} 4 | {$R 'hIcons.dcr'} 5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 6 | {$ALIGN 8} 7 | {$ASSERTIONS ON} 8 | {$BOOLEVAL OFF} 9 | {$DEBUGINFO OFF} 10 | {$EXTENDEDSYNTAX ON} 11 | {$IMPORTEDDATA ON} 12 | {$IOCHECKS ON} 13 | {$LOCALSYMBOLS ON} 14 | {$LONGSTRINGS ON} 15 | {$OPENSTRINGS ON} 16 | {$OPTIMIZATION OFF} 17 | {$OVERFLOWCHECKS ON} 18 | {$RANGECHECKS ON} 19 | {$REFERENCEINFO ON} 20 | {$SAFEDIVIDE OFF} 21 | {$STACKFRAMES ON} 22 | {$TYPEDADDRESS OFF} 23 | {$VARSTRINGCHECKS ON} 24 | {$WRITEABLECONST OFF} 25 | {$MINENUMSIZE 1} 26 | {$IMAGEBASE $400000} 27 | {$DEFINE DEBUG} 28 | {$ENDIF IMPLICITBUILDING} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | RESTComponents; 34 | 35 | contains 36 | OpenAI in 'OpenAI.pas', 37 | OpenAI.Types in 'OpenAI.Types.pas', 38 | OpenAI.API in 'OpenAI.API.pas', 39 | OpenAI.API.Params in 'OpenAI.API.Params.pas', 40 | OpenAI.Assistants in 'OpenAI.Assistants.pas', 41 | OpenAI.Audio in 'OpenAI.Audio.pas', 42 | OpenAI.Chat in 'OpenAI.Chat.pas', 43 | OpenAI.Chat.Functions in 'OpenAI.Chat.Functions.pas', 44 | OpenAI.Chat.Functions.Samples in 'OpenAI.Chat.Functions.Samples.pas', 45 | OpenAI.Completions in 'OpenAI.Completions.pas', 46 | OpenAI.Edits in 'OpenAI.Edits.pas', 47 | OpenAI.Embeddings in 'OpenAI.Embeddings.pas', 48 | OpenAI.Engines in 'OpenAI.Engines.pas', 49 | OpenAI.Errors in 'OpenAI.Errors.pas', 50 | OpenAI.Files in 'OpenAI.Files.pas', 51 | OpenAI.FineTunes in 'OpenAI.FineTunes.pas', 52 | OpenAI.FineTuning in 'OpenAI.FineTuning.pas', 53 | OpenAI.Images in 'OpenAI.Images.pas', 54 | OpenAI.Models in 'OpenAI.Models.pas', 55 | OpenAI.Moderations in 'OpenAI.Moderations.pas', 56 | OpenAI.Utils.Base64 in 'OpenAI.Utils.Base64.pas', 57 | OpenAI.Utils.ChatHistory in 'OpenAI.Utils.ChatHistory.pas', 58 | OpenAI.Utils.ObjectHolder in 'OpenAI.Utils.ObjectHolder.pas', 59 | OpenAI.Component.Chat in 'OpenAI.Component.Chat.pas', 60 | OpenAI.Component.Functions in 'OpenAI.Component.Functions.pas', 61 | OpenAI.Component.Reg in 'OpenAI.Component.Reg.pas', 62 | OpenAI.Utils.JSON.Cleaner in 'OpenAI.Utils.JSON.Cleaner.pas', 63 | OpenAI.Component.ChatHistory in 'OpenAI.Component.ChatHistory.pas'; 64 | 65 | end. 66 | 67 | 68 | -------------------------------------------------------------------------------- /OpenAI.Chat.Functions.Samples.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Chat.Functions.Samples; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.Chat.Functions; 7 | 8 | type 9 | TChatFunctionWeather = class(TChatFunction) 10 | protected 11 | function GetDescription: string; override; 12 | function GetName: string; override; 13 | function GetParameters: string; override; 14 | public 15 | constructor Create; override; 16 | function Execute(const Args: string): string; override; 17 | end; 18 | 19 | implementation 20 | 21 | uses 22 | System.JSON; 23 | 24 | { TChatFunctionWeather } 25 | 26 | constructor TChatFunctionWeather.Create; 27 | begin 28 | inherited; 29 | end; 30 | 31 | function TChatFunctionWeather.Execute(const Args: string): string; 32 | var 33 | JSON: TJSONObject; 34 | Location: string; 35 | UnitKind: string; 36 | begin 37 | Result := ''; 38 | Location := ''; 39 | UnitKind := ''; 40 | // Parse arguments 41 | try 42 | JSON := TJSONObject.ParseJSONValue(Args) as TJSONObject; 43 | if Assigned(JSON) then 44 | try 45 | Location := JSON.GetValue('location', ''); 46 | UnitKind := JSON.GetValue('unit', ''); 47 | finally 48 | JSON.Free; 49 | end; 50 | except 51 | Location := ''; 52 | end; 53 | // Invalid arguments 54 | if Location.IsEmpty then 55 | Exit; 56 | 57 | // Generate response 58 | JSON := TJSONObject.Create; 59 | try 60 | JSON.AddPair('location', Location); 61 | JSON.AddPair('unit', UnitKind); 62 | 63 | JSON.AddPair('temperature', TJSONNumber.Create(72)); 64 | JSON.AddPair('forecast', TJSONArray.Create('sunny', 'windy')); 65 | 66 | Result := JSON.ToJSON; 67 | finally 68 | JSON.Free; 69 | end; 70 | end; 71 | 72 | function TChatFunctionWeather.GetDescription: string; 73 | begin 74 | Result := 'Get the current weather in a given location'; 75 | end; 76 | 77 | function TChatFunctionWeather.GetName: string; 78 | begin 79 | Result := 'get_current_weather'; 80 | end; 81 | 82 | function TChatFunctionWeather.GetParameters: string; 83 | begin 84 | Result := // json scheme 85 | '{' + 86 | ' "type": "object",' + 87 | ' "properties": {' + 88 | ' "location": {"type": "string", "description": "The city and state, e.g. San Francisco, CA"},' + 89 | ' "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}' + 90 | ' },' + 91 | ' "required": ["location"]' + 92 | '}'; 93 | end; 94 | 95 | end. 96 | 97 | -------------------------------------------------------------------------------- /Samples/OpenAIGroup.groupproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | {AE251A2F-B3DA-4636-956F-25534F80B11A} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Default.Personality.12 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /OpenAI.Component.ChatHistory.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Component.ChatHistory; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, System.SyncObjs, System.Generics.Collections, 7 | System.Threading, OpenAI, OpenAI.Chat, OpenAI.Utils.ObjectHolder, 8 | OpenAI.Utils.ChatHistory; 9 | 10 | type 11 | TChat = OpenAI.Chat.TChat; 12 | 13 | TChatMessageBuild = OpenAI.Chat.TChatMessageBuild; 14 | 15 | TOpenAIChatHistoryCustom = class(TComponent) 16 | private 17 | function GetAutoTrim: Boolean; 18 | function GetMaxTokensForQuery: Int64; 19 | function GetMaxTokensOfModel: Int64; 20 | procedure SetAutoTrim(const Value: Boolean); 21 | procedure SetMaxTokensForQuery(const Value: Int64); 22 | procedure SetMaxTokensOfModel(const Value: Int64); 23 | protected 24 | FItems: TChatHistory; 25 | public 26 | constructor Create(AOwner: TComponent); override; 27 | destructor Destroy; override; 28 | property Items: TChatHistory read FItems; 29 | property AutoTrim: Boolean read GetAutoTrim write SetAutoTrim; 30 | property MaxTokensForQuery: Int64 read GetMaxTokensForQuery write SetMaxTokensForQuery; 31 | property MaxTokensOfModel: Int64 read GetMaxTokensOfModel write SetMaxTokensOfModel; 32 | end; 33 | 34 | TOpenAIChatHistory = class(TOpenAIChatHistoryCustom) 35 | published 36 | property AutoTrim; 37 | property MaxTokensForQuery; 38 | property MaxTokensOfModel; 39 | end; 40 | 41 | implementation 42 | 43 | { TOpenAIChatHistoryCustom } 44 | 45 | constructor TOpenAIChatHistoryCustom.Create(AOwner: TComponent); 46 | begin 47 | inherited; 48 | FItems := TChatHistory.Create; 49 | end; 50 | 51 | destructor TOpenAIChatHistoryCustom.Destroy; 52 | begin 53 | FItems.Free; 54 | inherited; 55 | end; 56 | 57 | function TOpenAIChatHistoryCustom.GetAutoTrim: Boolean; 58 | begin 59 | Result := FItems.AutoTrim; 60 | end; 61 | 62 | function TOpenAIChatHistoryCustom.GetMaxTokensForQuery: Int64; 63 | begin 64 | Result := FItems.MaxTokensForQuery; 65 | end; 66 | 67 | function TOpenAIChatHistoryCustom.GetMaxTokensOfModel: Int64; 68 | begin 69 | Result := FItems.MaxTokensOfModel; 70 | end; 71 | 72 | procedure TOpenAIChatHistoryCustom.SetAutoTrim(const Value: Boolean); 73 | begin 74 | FItems.AutoTrim := Value; 75 | end; 76 | 77 | procedure TOpenAIChatHistoryCustom.SetMaxTokensForQuery(const Value: Int64); 78 | begin 79 | FItems.MaxTokensForQuery := Value; 80 | end; 81 | 82 | procedure TOpenAIChatHistoryCustom.SetMaxTokensOfModel(const Value: Int64); 83 | begin 84 | FItems.MaxTokensOfModel := Value; 85 | end; 86 | 87 | end. 88 | 89 | -------------------------------------------------------------------------------- /OpenAI.GigaChat.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.GigaChat; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, OpenAI.API; 7 | 8 | type 9 | TApiPrepareGigaChat = class(TInterfacedObject, IAPIPrepare) 10 | const 11 | SBER_GIGACHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth'; 12 | SBER_GIGACHAT_OAUTH_SCOPE = 'GIGACHAT_API_PERS'; 13 | private 14 | FClientID: string; 15 | FAuthorizationKey: string; 16 | FAccessToken: string; 17 | FExpiresAt: TDateTime; 18 | function IsValidToken: Boolean; 19 | procedure UpdateToken(API: TOpenAIAPI); 20 | public 21 | procedure PrepareQuery(API: TOpenAIAPI); 22 | constructor Create(const ClientID, AuthorizationKey: string); reintroduce; 23 | end; 24 | 25 | implementation 26 | 27 | uses 28 | System.Net.URLClient, System.JSON, System.DateUtils, System.NetConsts, 29 | System.Net.HttpClient; 30 | 31 | { TApiPrepareGigaChat } 32 | 33 | constructor TApiPrepareGigaChat.Create(const ClientID, AuthorizationKey: string); 34 | begin 35 | inherited Create; 36 | FClientID := ClientID; 37 | FAuthorizationKey := AuthorizationKey; 38 | end; 39 | 40 | function TApiPrepareGigaChat.IsValidToken: Boolean; 41 | begin 42 | Result := (not FAccessToken.IsEmpty) and (FExpiresAt > Now); 43 | end; 44 | 45 | procedure TApiPrepareGigaChat.PrepareQuery(API: TOpenAIAPI); 46 | begin 47 | if not IsValidToken then 48 | UpdateToken(API); 49 | API.Token := FAccessToken; 50 | end; 51 | 52 | procedure TApiPrepareGigaChat.UpdateToken(API: TOpenAIAPI); 53 | begin 54 | var Client := API.GetClient; 55 | try 56 | Client.Accept := 'application/json'; 57 | Client.CustomHeaders['RqUID'] := FClientID; 58 | Client.CustomHeaders['Authorization'] := 'Basic ' + FAuthorizationKey; 59 | var Content := TStringList.Create; 60 | try 61 | Content.AddPair('scope', SBER_GIGACHAT_OAUTH_SCOPE); 62 | var Response := Client.Post(SBER_GIGACHAT_OAUTH, Content); 63 | if Response.StatusCode <> 200 then 64 | raise OpenAIPrepareException.Create(Response.StatusText, '', '', Response.StatusCode); 65 | var JSON := TJSONObject.ParseJSONValue(Response.ContentAsString); 66 | try 67 | FAccessToken := JSON.GetValue('access_token', ''); 68 | FExpiresAt := UnixToDateTime(JSON.GetValue('expires_at', 0) div MSecsPerSec, False); 69 | if not IsValidToken then 70 | raise OpenAIPrepareException.Create('Token receipt error', '', '', Response.StatusCode); 71 | finally 72 | JSON.Free; 73 | end; 74 | finally 75 | Content.Free; 76 | end; 77 | finally 78 | Client.Free; 79 | end; 80 | end; 81 | 82 | end. 83 | 84 | -------------------------------------------------------------------------------- /OpenAI.Utils.ObjectHolder.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Utils.ObjectHolder; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.SysUtils, System.Threading; 7 | 8 | type 9 | THolder = class(TComponent) 10 | private 11 | FHold: TComponent; 12 | protected 13 | procedure Notification(AComponent: TComponent; Operation: TOperation); override; 14 | public 15 | procedure HoldComponent(AComponent: TComponent); 16 | function IsLive: Boolean; 17 | end; 18 | 19 | IComponentHolder = interface 20 | ['{FBCA20CB-E6BB-403B-877C-230F4D343A17}'] 21 | procedure HoldComponent(AComponent: TComponent); 22 | function IsLive: Boolean; 23 | end; 24 | 25 | TComponentHolder = class(TInterfacedObject, IComponentHolder) 26 | private 27 | FHolder: THolder; 28 | public 29 | procedure HoldComponent(AComponent: TComponent); 30 | function IsLive: Boolean; 31 | constructor Create(AComponent: TComponent = nil); 32 | destructor Destroy; override; 33 | end; 34 | 35 | function TaskRun(const Owner: TComponent; Proc: TProc): ITask; 36 | 37 | procedure Queue(Proc: TThreadProcedure); 38 | 39 | procedure Sync(Proc: TThreadProcedure); 40 | 41 | implementation 42 | 43 | function TaskRun(const Owner: TComponent; Proc: TProc): ITask; 44 | var 45 | ObjectHold: IComponentHolder; 46 | begin 47 | ObjectHold := TComponentHolder.Create(Owner); 48 | Result := TTask.Run( 49 | procedure 50 | begin 51 | Proc(ObjectHold); 52 | end); 53 | end; 54 | 55 | procedure Queue(Proc: TThreadProcedure); 56 | begin 57 | TThread.Queue(nil, Proc); 58 | end; 59 | 60 | procedure Sync(Proc: TThreadProcedure); 61 | begin 62 | TThread.Synchronize(nil, Proc); 63 | end; 64 | 65 | { THolder } 66 | 67 | procedure THolder.HoldComponent(AComponent: TComponent); 68 | begin 69 | FHold := AComponent; 70 | AComponent.FreeNotification(Self); 71 | end; 72 | 73 | function THolder.IsLive: Boolean; 74 | begin 75 | Result := Assigned(FHold); 76 | end; 77 | 78 | procedure THolder.Notification(AComponent: TComponent; Operation: TOperation); 79 | begin 80 | inherited; 81 | if Operation = opRemove then 82 | if AComponent = FHold then 83 | FHold := nil; 84 | end; 85 | 86 | { TComponentHolder } 87 | 88 | constructor TComponentHolder.Create(AComponent: TComponent); 89 | begin 90 | inherited Create; 91 | FHolder := THolder.Create(nil); 92 | FHolder.HoldComponent(AComponent); 93 | end; 94 | 95 | destructor TComponentHolder.Destroy; 96 | begin 97 | FHolder.Free; 98 | inherited; 99 | end; 100 | 101 | procedure TComponentHolder.HoldComponent(AComponent: TComponent); 102 | begin 103 | FHolder.HoldComponent(AComponent); 104 | end; 105 | 106 | function TComponentHolder.IsLive: Boolean; 107 | begin 108 | Result := FHolder.IsLive; 109 | end; 110 | 111 | end. 112 | 113 | -------------------------------------------------------------------------------- /Samples/Sample/SampleGPT.dpr: -------------------------------------------------------------------------------- 1 | program SampleGPT; 2 | 3 | uses 4 | System.SysUtils, 5 | OpenAI, 6 | OpenAI.GigaChat; 7 | 8 | procedure OpenAI; 9 | begin 10 | var client := TOpenAI.Create(''); 11 | var response := client.Chat.Create( 12 | procedure(Params: TChatParams) 13 | begin 14 | Params.Model('gpt-4o'); 15 | Params.Messages([ 16 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 17 | ]); 18 | end); 19 | print(response.Choices[0].Message.Content); 20 | end; 21 | 22 | procedure DeepSeek; 23 | begin 24 | var client := TOpenAI.Create( 25 | 'https://api.deepseek.com', 26 | ''); 27 | var response := client.Chat.Create( 28 | procedure(Params: TChatParams) 29 | begin 30 | Params.Model('deepseek-chat'); 31 | Params.Messages([ 32 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 33 | ]); 34 | end); 35 | print(response.Choices[0].Message.Content); 36 | end; 37 | 38 | procedure YandexGPT_ApiKey; 39 | begin 40 | var client := TOpenAI.Create( 41 | 'https://llm.api.cloud.yandex.net/v1', 42 | 'api-key '); 43 | client.DisableBearerPrefix := True; 44 | var response := client.Chat.Create( 45 | procedure(Params: TChatParams) 46 | begin 47 | Params.Model('gpt://b1gclqm3lu4itq271tvj/yandexgpt/latest'); 48 | Params.Messages([ 49 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 50 | ]); 51 | end); 52 | print(response.Choices[0].Message.Content); 53 | end; 54 | 55 | procedure YandexGPT; 56 | begin 57 | var client := TOpenAI.Create( 58 | 'https://llm.api.cloud.yandex.net/v1', 59 | ''); 60 | var response := client.Chat.Create( 61 | procedure(Params: TChatParams) 62 | begin 63 | Params.Model('gpt://b1gclqm3lu4itq271tvj/yandexgpt/latest'); 64 | Params.Messages([ 65 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 66 | ]); 67 | end); 68 | print(response.Choices[0].Message.Content); 69 | end; 70 | 71 | procedure Qwen; 72 | begin 73 | var client := TOpenAI.Create( 74 | 'https://api.aimlapi.com/v1', 75 | ''); 76 | var response := client.Chat.Create( 77 | procedure(Params: TChatParams) 78 | begin 79 | Params.Model('gpt-4o'); 80 | Params.Messages([ 81 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 82 | ]); 83 | end); 84 | print(response.Choices[0].Message.Content); 85 | end; 86 | 87 | procedure GigaChat; 88 | begin 89 | var client := TOpenAI.Create( 90 | 'https://gigachat.devices.sberbank.ru/api/v1', ''); 91 | client.Prepare := TApiPrepareGigaChat.Create( 92 | '', 93 | ''); 94 | var response := client.Chat.Create( 95 | procedure(Params: TChatParams) 96 | begin 97 | Params.Model('GigaChat'); 98 | Params.Messages([ 99 | TChatMessageUser.Create.Content('Write a one-sentence story about numbers.') 100 | ]); 101 | end); 102 | print(response.Choices[0].Message.Content); 103 | end; 104 | 105 | begin 106 | try 107 | //OpenAI; 108 | //YandexGPT_ApiKey; 109 | //YandexGPT; 110 | //Qwen; 111 | //DeepSeek; 112 | //GigaChat; 113 | except 114 | on E: Exception do 115 | Writeln(E.Message); 116 | end; 117 | readln; 118 | end. 119 | 120 | -------------------------------------------------------------------------------- /Samples/Chat/SimpleChat.gv: -------------------------------------------------------------------------------- 1 | digraph SimpleChat { 2 | SimpleChat -> { "Chat.Main" "OpenAI.API.Params" "OpenAI.API" "OpenAI.Audio" "OpenAI.Chat.Functions" "OpenAI.Chat.Functions.Samples" "OpenAI.Chat" "OpenAI.Completions" "OpenAI.Component.Chat" "OpenAI.Edits" "OpenAI.Embeddings" "OpenAI.Engines" "OpenAI.Errors" "OpenAI.Files" "OpenAI.FineTunes" "OpenAI.FineTuning" "OpenAI.Images" "OpenAI.Models" "OpenAI.Moderations" OpenAI "OpenAI.Utils.ChatHistory" "OpenAI.Component.Functions" } 3 | "Chat.Main" -> { "REST.Types" "Data.Bind.Components" "Data.Bind.ObjectScope" "REST.Client" OpenAI "OpenAI.Component.Chat" "OpenAI.Component.Functions" "OpenAI.Chat" } 4 | "REST.Types" -> "REST.Consts" [arrowhead=open,style=dashed] 5 | "REST.Consts" 6 | "Data.Bind.Components" -> { "Data.Bind.Consts" "Data.Bind.ObserverLinks" "Data.Bind.Editors" } [arrowhead=open,style=dashed] 7 | "Data.Bind.Consts" 8 | "Data.Bind.ObserverLinks" -> "Data.Bind.Components" 9 | "Data.Bind.ObserverLinks" -> "Data.Bind.Consts" [arrowhead=open,style=dashed] 10 | "Data.Bind.Editors" -> "Data.Bind.Components" 11 | "Data.Bind.Editors" -> "Data.Bind.Grid" [arrowhead=open,style=dashed] 12 | "Data.Bind.Grid" -> "Data.Bind.Components" 13 | "Data.Bind.Grid" -> "Data.Bind.Consts" [arrowhead=open,style=dashed] 14 | "Data.Bind.ObjectScope" -> "Data.Bind.Components" 15 | "Data.Bind.ObjectScope" -> "Data.Bind.Consts" [arrowhead=open,style=dashed] 16 | "REST.Client" -> { "Data.Bind.ObjectScope" "Data.Bind.Components" "REST.HttpClient" "REST.Types" "REST.BindSource" } 17 | "REST.Client" -> { "Data.Bind.JSON" "REST.Json" "REST.Consts" "REST.Utils" } [arrowhead=open,style=dashed] 18 | "REST.HttpClient" 19 | "REST.BindSource" -> { "Data.Bind.Components" "Data.Bind.ObjectScope" } 20 | "REST.BindSource" -> { "REST.Consts" "REST.Types" } [arrowhead=open,style=dashed] 21 | "Data.Bind.JSON" 22 | "REST.Json" -> { "REST.Json.Types" "REST.JsonReflect" } 23 | "REST.Json.Types" -> "REST.Consts" 24 | "REST.JsonReflect" -> "REST.Json.Types" 25 | "REST.JsonReflect" -> "REST.Json.Interceptors" [arrowhead=open,style=dashed] 26 | "REST.Json.Interceptors" -> "REST.JsonReflect" 27 | "REST.Json.Interceptors" -> "REST.Json.Types" [arrowhead=open,style=dashed] 28 | "REST.Utils" 29 | OpenAI -> { "OpenAI.Completions" "OpenAI.Edits" "OpenAI.Images" "OpenAI.Models" "OpenAI.Embeddings" "OpenAI.API" "OpenAI.Moderations" "OpenAI.Engines" "OpenAI.Files" "OpenAI.FineTunes" "OpenAI.Chat" "OpenAI.Audio" "OpenAI.FineTuning" } 30 | "OpenAI.Completions" -> { "OpenAI.API.Params" "OpenAI.API" } 31 | "OpenAI.Completions" -> "REST.Json" [arrowhead=open,style=dashed] 32 | "OpenAI.API.Params" -> { "REST.JsonReflect" "REST.Json.Interceptors" } 33 | "OpenAI.API" -> { "OpenAI.Errors" "OpenAI.API.Params" } 34 | "OpenAI.API" -> "REST.Json" [arrowhead=open,style=dashed] 35 | "OpenAI.Errors" 36 | "OpenAI.Edits" -> { "OpenAI.API.Params" "OpenAI.API" } 37 | "OpenAI.Images" -> { "OpenAI.API.Params" "OpenAI.API" } 38 | "OpenAI.Models" -> "OpenAI.API" 39 | "OpenAI.Embeddings" -> { "OpenAI.API.Params" "OpenAI.API" } 40 | "OpenAI.Moderations" -> { "OpenAI.API.Params" "OpenAI.API" "REST.Json.Types" } 41 | "OpenAI.Engines" -> "OpenAI.API" 42 | "OpenAI.Files" -> { "OpenAI.API.Params" "OpenAI.API" } 43 | "OpenAI.FineTunes" -> { "OpenAI.API" "OpenAI.API.Params" "OpenAI.Files" } 44 | "OpenAI.Chat" -> { "OpenAI.API.Params" "OpenAI.API" "OpenAI.Chat.Functions" "REST.JsonReflect" } 45 | "OpenAI.Chat" -> "REST.Json" [arrowhead=open,style=dashed] 46 | "OpenAI.Chat.Functions" 47 | "OpenAI.Audio" -> { "OpenAI.API.Params" "OpenAI.API" } 48 | "OpenAI.FineTuning" -> { "REST.Json" "OpenAI.API" "OpenAI.API.Params" } 49 | "OpenAI.Component.Chat" -> { OpenAI "OpenAI.Chat" "OpenAI.Component.Functions" } 50 | "OpenAI.Component.Chat" -> "OpenAI.Chat.Functions" [arrowhead=open,style=dashed] 51 | "OpenAI.Component.Functions" -> "OpenAI.Chat.Functions" 52 | "OpenAI.Chat.Functions.Samples" -> "OpenAI.Chat.Functions" 53 | "OpenAI.Utils.ChatHistory" -> "OpenAI.Chat" 54 | } 55 | -------------------------------------------------------------------------------- /OpenAI.Chat.Functions.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Chat.Functions; 2 | 3 | interface 4 | 5 | uses 6 | System.JSON; 7 | 8 | type 9 | IChatFunction = interface 10 | ['{F2B4D026-5FA9-4499-B5D1-3FEA4885C511}'] 11 | function GetDescription: string; 12 | function GetName: string; 13 | function GetParameters: string; 14 | function GetStrict: Boolean; 15 | function Execute(const Args: string): string; 16 | /// 17 | /// A description of what the function does, used by the model to choose when and how to call the function. 18 | /// 19 | property Description: string read GetDescription; 20 | /// 21 | /// The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, 22 | /// with a maximum length of 64. 23 | /// 24 | property Name: string read GetName; 25 | /// 26 | /// The parameters the functions accepts, described as a JSON Schema object. 27 | /// See the guide for examples, and the JSON Schema reference for documentation about the format. 28 | /// 29 | /// To describe a function that accepts no parameters, provide the value {"type": "object", "properties": {}}. 30 | /// 31 | /// https://json-schema.org/understanding-json-schema/ 32 | property Parameters: string read GetParameters; 33 | /// 34 | /// Whether to enable strict schema adherence when generating the function call. 35 | /// If set to true, the model will follow the exact schema defined in the parameters field. 36 | /// Only a subset of JSON Schema is supported when strict is true. 37 | /// Learn more about Structured Outputs in the function calling guide. 38 | /// 39 | /// https://platform.openai.com/docs/api-reference/chat/docs/guides/function-calling 40 | property strict: Boolean read GetStrict; 41 | end; 42 | 43 | TChatFunction = class abstract(TInterfacedObject, IChatFunction) 44 | protected 45 | function GetDescription: string; virtual; abstract; 46 | function GetName: string; virtual; abstract; 47 | function GetParameters: string; virtual; abstract; 48 | function GetStrict: Boolean; virtual; 49 | public 50 | /// 51 | /// A description of what the function does, used by the model to choose when and how to call the function. 52 | /// 53 | property Description: string read GetDescription; 54 | /// 55 | /// The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, 56 | /// with a maximum length of 64. 57 | /// 58 | property Name: string read GetName; 59 | /// 60 | /// The parameters the functions accepts, described as a JSON Schema object. 61 | /// See the guide for examples, and the JSON Schema reference for documentation about the format. 62 | /// 63 | /// To describe a function that accepts no parameters, provide the value {"type": "object", "properties": {}}. 64 | /// 65 | /// https://json-schema.org/understanding-json-schema/ 66 | property Parameters: string read GetParameters; 67 | /// 68 | /// Whether to enable strict schema adherence when generating the function call. 69 | /// If set to true, the model will follow the exact schema defined in the parameters field. 70 | /// Only a subset of JSON Schema is supported when strict is true. 71 | /// Learn more about Structured Outputs in the function calling guide. 72 | /// 73 | /// https://platform.openai.com/docs/api-reference/chat/docs/guides/function-calling 74 | property strict: Boolean read GetStrict; 75 | function Execute(const Args: string): string; virtual; 76 | class function ToJson(Value: IChatFunction): TJSONObject; 77 | constructor Create; virtual; 78 | end; 79 | 80 | implementation 81 | 82 | uses 83 | System.SysUtils; 84 | 85 | { TChatFunction } 86 | 87 | constructor TChatFunction.Create; 88 | begin 89 | inherited; 90 | end; 91 | 92 | function TChatFunction.Execute(const Args: string): string; 93 | begin 94 | Result := ''; 95 | end; 96 | 97 | function TChatFunction.GetStrict: Boolean; 98 | begin 99 | Result := False; 100 | end; 101 | 102 | class function TChatFunction.ToJson(Value: IChatFunction): TJSONObject; 103 | begin 104 | Result := TJSONObject.Create; 105 | try 106 | Result.AddPair('name', Value.GetName); 107 | Result.AddPair('description', Value.GetDescription); 108 | Result.AddPair('parameters', TJSONObject.ParseJSONValue(Value.GetParameters)); 109 | except 110 | on E: Exception do 111 | begin 112 | Result.Free; 113 | raise; 114 | end; 115 | end; 116 | end; 117 | 118 | end. 119 | 120 | -------------------------------------------------------------------------------- /OpenAI.Models.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Models; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API, OpenAI.Types; 7 | 8 | type 9 | TModelPermission = class 10 | private 11 | FAllow_create_engine: Boolean; 12 | FAllow_fine_tuning: Boolean; 13 | FAllow_logprobs: Boolean; 14 | FAllow_sampling: Boolean; 15 | FAllow_search_indices: Boolean; 16 | FAllow_view: Boolean; 17 | FCreated: Int64; 18 | FId: string; 19 | FIs_blocking: Boolean; 20 | FObject: string; 21 | FOrganization: string; 22 | public 23 | property AllowCreateEngine: Boolean read FAllow_create_engine write FAllow_create_engine; 24 | property AllowFineTuning: Boolean read FAllow_fine_tuning write FAllow_fine_tuning; 25 | property AllowLogprobs: Boolean read FAllow_logprobs write FAllow_logprobs; 26 | property AllowSampling: Boolean read FAllow_sampling write FAllow_sampling; 27 | property AllowSearchIndices: Boolean read FAllow_search_indices write FAllow_search_indices; 28 | property AllowView: Boolean read FAllow_view write FAllow_view; 29 | property Created: Int64 read FCreated write FCreated; 30 | property Id: string read FId write FId; 31 | property IsBlocking: Boolean read FIs_blocking write FIs_blocking; 32 | property &Object: string read FObject write FObject; 33 | property Organization: string read FOrganization write FOrganization; 34 | end; 35 | 36 | /// 37 | /// Describes an OpenAI model offering that can be used with the API. 38 | /// 39 | TModel = class 40 | private 41 | FCreated: Int64; 42 | FId: string; 43 | FObject: string; 44 | FOwned_by: string; 45 | FPermission: TArray; 46 | FRoot: string; 47 | public 48 | /// 49 | /// The Unix timestamp (in seconds) when the model was created. 50 | /// 51 | property Created: Int64 read FCreated write FCreated; 52 | /// 53 | /// The model identifier, which can be referenced in the API endpoints. 54 | /// 55 | property Id: string read FId write FId; 56 | /// 57 | /// The object type, which is always "model". 58 | /// 59 | property &Object: string read FObject write FObject; 60 | /// 61 | /// The organization that owns the model. 62 | /// 63 | property OwnedBy: string read FOwned_by write FOwned_by; 64 | /// 65 | /// May be depricated 66 | /// 67 | property Permission: TArray read FPermission write FPermission; 68 | /// 69 | /// May be depricated 70 | /// 71 | property Root: string read FRoot write FRoot; 72 | destructor Destroy; override; 73 | end; 74 | 75 | /// 76 | /// Lists the currently available models, and provides basic information about each one such as the owner and availability. 77 | /// 78 | TModels = class 79 | private 80 | FData: TArray; 81 | FObject: string; 82 | public 83 | property Data: TArray read FData write FData; 84 | property &Object: string read FObject write FObject; 85 | destructor Destroy; override; 86 | end; 87 | 88 | TModelsRoute = class(TOpenAIAPIRoute) 89 | public 90 | /// 91 | /// Lists the currently available models, and provides basic information about each one such as the owner and availability. 92 | /// 93 | function List: TModels; 94 | /// 95 | /// Retrieves a model instance, providing basic information about the model such as the owner and permissioning. 96 | /// 97 | /// The ID of the model to use for this request 98 | function Retrieve(const Model: string): TModel; 99 | /// 100 | /// Delete a fine-tuned model. You must have the Owner role in your organization to delete a model. 101 | /// 102 | function DeleteFineTuneModel(const Model: string): TDeletionStatus; 103 | end; 104 | 105 | implementation 106 | 107 | { TModelsRoute } 108 | 109 | function TModelsRoute.DeleteFineTuneModel(const Model: string): TDeletionStatus; 110 | begin 111 | Result := API.Delete('models/' + Model); 112 | end; 113 | 114 | function TModelsRoute.List: TModels; 115 | begin 116 | Result := API.Get('models'); 117 | end; 118 | 119 | function TModelsRoute.Retrieve(const Model: string): TModel; 120 | begin 121 | Result := API.Get('models/' + Model); 122 | end; 123 | 124 | { TModels } 125 | 126 | destructor TModels.Destroy; 127 | var 128 | Item: TModel; 129 | begin 130 | for Item in FData do 131 | Item.Free; 132 | inherited; 133 | end; 134 | 135 | { TModel } 136 | 137 | destructor TModel.Destroy; 138 | var 139 | Item: TModelPermission; 140 | begin 141 | for Item in FPermission do 142 | Item.Free; 143 | inherited; 144 | end; 145 | 146 | end. 147 | 148 | -------------------------------------------------------------------------------- /Samples/Chat/Chat.Main.fmx: -------------------------------------------------------------------------------- 1 | object FormChat: TFormChat 2 | Left = 0 3 | Top = 0 4 | Caption = 'Simple Chat GPT' 5 | ClientHeight = 395 6 | ClientWidth = 641 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnCreate = FormCreate 11 | DesignerMasterStyle = 0 12 | object MemoMessages: TMemo 13 | Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] 14 | DataDetectorTypes = [] 15 | TextSettings.WordWrap = True 16 | Position.X = 8.000000000000000000 17 | Position.Y = 8.000000000000000000 18 | Size.Width = 625.000000000000000000 19 | Size.Height = 217.000000000000000000 20 | Size.PlatformDefault = False 21 | TabOrder = 2 22 | Viewport.Width = 621.000000000000000000 23 | Viewport.Height = 213.000000000000000000 24 | end 25 | object MemoMessage: TMemo 26 | Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] 27 | DataDetectorTypes = [] 28 | Lines.Strings = ( 29 | 'Weather in Yekaterinburg') 30 | TextSettings.WordWrap = True 31 | Position.X = 8.000000000000000000 32 | Position.Y = 337.000000000000000000 33 | Size.Width = 625.000000000000000000 34 | Size.Height = 50.000000000000000000 35 | Size.PlatformDefault = False 36 | TabOrder = 3 37 | Viewport.Width = 621.000000000000000000 38 | Viewport.Height = 46.000000000000000000 39 | end 40 | object ButtonSend: TButton 41 | Position.X = 553.000000000000000000 42 | Position.Y = 233.000000000000000000 43 | Size.Width = 80.000000000000000000 44 | Size.Height = 24.000000000000000000 45 | Size.PlatformDefault = False 46 | TabOrder = 5 47 | Text = 'Send' 48 | TextSettings.Trimming = None 49 | OnClick = ButtonSendClick 50 | end 51 | object ButtonStreamSend: TButton 52 | Position.X = 553.000000000000000000 53 | Position.Y = 265.000000000000000000 54 | Size.Width = 80.000000000000000000 55 | Size.Height = 24.000000000000000000 56 | Size.PlatformDefault = False 57 | TabOrder = 4 58 | Text = 'StreamSend' 59 | TextSettings.Trimming = None 60 | OnClick = ButtonStreamSendClick 61 | end 62 | object AniIndicatorBusy: TAniIndicator 63 | Align = Center 64 | Enabled = True 65 | Visible = False 66 | end 67 | object ListBox1: TListBox 68 | Position.X = 8.000000000000000000 69 | Position.Y = 233.000000000000000000 70 | Size.Width = 505.000000000000000000 71 | Size.Height = 96.000000000000000000 72 | Size.PlatformDefault = False 73 | TabOrder = 10 74 | DisableFocusEffect = True 75 | ItemHeight = 75.000000000000000000 76 | ItemWidth = 75.000000000000000000 77 | DefaultItemStyles.ItemStyle = 'listboxitemstyle_image' 78 | DefaultItemStyles.GroupHeaderStyle = '' 79 | DefaultItemStyles.GroupFooterStyle = '' 80 | ListStyle = Horizontal 81 | Viewport.Width = 501.000000000000000000 82 | Viewport.Height = 92.000000000000000000 83 | end 84 | object ButtonAttach: TButton 85 | Hint = 'Attach file' 86 | Position.X = 521.000000000000000000 87 | Position.Y = 233.000000000000000000 88 | Size.Width = 24.000000000000000000 89 | Size.Height = 24.000000000000000000 90 | Size.PlatformDefault = False 91 | TabOrder = 12 92 | Text = '+' 93 | TextSettings.Trimming = None 94 | OnClick = ButtonAttachClick 95 | end 96 | object ButtonRemoveAttach: TButton 97 | Hint = 'Remove attach' 98 | Position.X = 521.000000000000000000 99 | Position.Y = 265.000000000000000000 100 | Size.Width = 24.000000000000000000 101 | Size.Height = 24.000000000000000000 102 | Size.PlatformDefault = False 103 | TabOrder = 11 104 | Text = '-' 105 | TextSettings.Trimming = None 106 | end 107 | object OpenDialogImg: TOpenDialog 108 | Left = 400 109 | Top = 32 110 | end 111 | object OpenAIClient1: TOpenAIClient 112 | BaseURL = 'https://api.openai.com/v1' 113 | Left = 40 114 | Top = 32 115 | end 116 | object OpenAIChat1: TOpenAIChat 117 | Client = OpenAIClient1 118 | History = OpenAIChatHistory1 119 | Functions = OpenAIChatFunctions1 120 | Model = 'gpt-4o' 121 | Temperature = 1.000000000000000000 122 | TopP = 1.000000000000000000 123 | MaxTokens = 10000 124 | OnChat = OpenAIChat1Chat 125 | OnChatDelta = OpenAIChat1ChatDelta 126 | OnError = OpenAIChat1Error 127 | OnBeginWork = OpenAIChat1BeginWork 128 | OnEndWork = OpenAIChat1EndWork 129 | Left = 136 130 | Top = 32 131 | end 132 | object OpenAIChatFunctions1: TOpenAIChatFunctions 133 | Items = < 134 | item 135 | Description = 'Get the current weather in a given location' 136 | Name = 'get_current_weather' 137 | Parameters = 138 | '{'#13#10' "type": "object",'#13#10' "properties": {'#13#10' "location": {"t' + 139 | 'ype": "string", "description": "The city and state, e.g. San Fra' + 140 | 'ncisco, CA"},'#13#10' "unit": {"type": "string", "enum": ["celsiu' + 141 | 's", "fahrenheit"]}'#13#10' },'#13#10' "required": ["location"]'#13#10'}' 142 | Strict = True 143 | OnFunctionExecute = FuncGetCurrentWeather 144 | end 145 | item 146 | Description = 'Get my computer'#39's information' 147 | Name = 'get_pc_info' 148 | Strict = False 149 | OnFunctionExecute = OpenAIChatFunctions1Items1FunctionExecute 150 | end> 151 | Left = 256 152 | Top = 32 153 | end 154 | object OpenAIChatHistory1: TOpenAIChatHistory 155 | AutoTrim = False 156 | MaxTokensForQuery = 1024 157 | MaxTokensOfModel = 4096 158 | Left = 256 159 | Top = 96 160 | end 161 | end 162 | -------------------------------------------------------------------------------- /OpenAI.Edits.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Edits; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API.Params, OpenAI.API; 7 | 8 | type 9 | TEditParams = class(TJSONParam) 10 | /// 11 | /// ID of the model to use. You can use the "text-davinci-edit-001" or 12 | /// "code-davinci-edit-001" model with this endpoint. 13 | /// 14 | function Model(const Value: string): TEditParams; 15 | /// 16 | /// The input text to use as a starting point for the edit. 17 | /// 18 | function Input(const Value: string): TEditParams; overload; 19 | /// 20 | /// The instruction that tells the model how to edit the prompt. 21 | /// 22 | function Instruction(const Value: string): TEditParams; overload; 23 | /// 24 | /// What sampling temperature to use, between 0 and 2. 25 | /// Higher values like 0.8 will make the output more random, 26 | /// while lower values like 0.2 will make it more focused and deterministic. 27 | /// We generally recommend altering this or top_p but not both. 28 | /// 29 | function Temperature(const Value: Single = 1): TEditParams; 30 | /// 31 | /// An alternative to sampling with temperature, called nucleus sampling, 32 | /// where the model considers the results of the tokens with top_p probability mass. 33 | /// So 0.1 means only the tokens comprising the top 10% probability mass are considered. 34 | /// We generally recommend altering this or temperature but not both. 35 | /// 36 | function TopP(const Value: Single = 1): TEditParams; 37 | /// 38 | /// How many edits to generate for the input and instruction. 39 | /// 40 | function N(const Value: Integer = 1): TEditParams; 41 | constructor Create; override; 42 | end; 43 | 44 | TEditUsage = class 45 | private 46 | FCompletion_tokens: Int64; 47 | FPrompt_tokens: Int64; 48 | FTotal_tokens: Int64; 49 | public 50 | /// 51 | /// Number of tokens in the generated completion. 52 | /// 53 | property CompletionTokens: Int64 read FCompletion_tokens write FCompletion_tokens; 54 | /// 55 | /// Number of tokens in the prompt. 56 | /// 57 | property PromptTokens: Int64 read FPrompt_tokens write FPrompt_tokens; 58 | /// 59 | /// Total number of tokens used in the request (prompt + completion). 60 | /// 61 | property TotalTokens: Int64 read FTotal_tokens write FTotal_tokens; 62 | end; 63 | 64 | TEditChoices = class 65 | private 66 | FIndex: Int64; 67 | FText: string; 68 | FFinish_reason: string; 69 | public 70 | /// 71 | /// The index of the choice in the list of choices. 72 | /// 73 | property Index: Int64 read FIndex write FIndex; 74 | /// 75 | /// The edited result. 76 | /// 77 | property Text: string read FText write FText; 78 | /// 79 | /// The reason the model stopped generating tokens. 80 | /// This will be "stop" if the model hit a natural stop point or a provided stop sequence, 81 | /// "length" if the maximum number of tokens specified in the request was reached, 82 | /// or "content_filter" if content was omitted due to a flag from our content filters. 83 | /// 84 | property FinishReason: string read FFinish_reason write FFinish_reason; 85 | end; 86 | 87 | TEdits = class 88 | private 89 | FChoices: TArray; 90 | FCreated: Int64; 91 | FObject: string; 92 | FUsage: TEditUsage; 93 | public 94 | /// 95 | /// The object type, which is always "edit". 96 | /// 97 | property &Object: string read FObject write FObject; 98 | /// 99 | /// A list of edit choices. Can be more than one if n is greater than 1. 100 | /// 101 | property Choices: TArray read FChoices write FChoices; 102 | /// 103 | /// The Unix timestamp (in seconds) of when the edit was created. 104 | /// 105 | property Created: Int64 read FCreated write FCreated; 106 | /// 107 | /// Usage statistics for the completion request. 108 | /// 109 | property Usage: TEditUsage read FUsage write FUsage; 110 | destructor Destroy; override; 111 | end; 112 | 113 | TEditsRoute = class(TOpenAIAPIRoute) 114 | public 115 | /// 116 | /// Creates a new edit for the provided input, instruction, and parameters 117 | /// 118 | function Create(ParamProc: TProc): TEdits; deprecated; 119 | end; 120 | 121 | implementation 122 | 123 | { TEditsRoute } 124 | 125 | function TEditsRoute.Create(ParamProc: TProc): TEdits; 126 | begin 127 | Result := API.Post('edits', ParamProc); 128 | end; 129 | 130 | { TEdits } 131 | 132 | destructor TEdits.Destroy; 133 | var 134 | Item: TEditChoices; 135 | begin 136 | if Assigned(FUsage) then 137 | FUsage.Free; 138 | for Item in FChoices do 139 | Item.Free; 140 | inherited; 141 | end; 142 | 143 | { TEditParams } 144 | 145 | constructor TEditParams.Create; 146 | begin 147 | inherited; 148 | Model('text-davinci-edit-001'); 149 | end; 150 | 151 | function TEditParams.Model(const Value: string): TEditParams; 152 | begin 153 | Result := TEditParams(Add('model', Value)); 154 | end; 155 | 156 | function TEditParams.N(const Value: Integer): TEditParams; 157 | begin 158 | Result := TEditParams(Add('n', Value)); 159 | end; 160 | 161 | function TEditParams.Input(const Value: string): TEditParams; 162 | begin 163 | Result := TEditParams(Add('input', Value)); 164 | end; 165 | 166 | function TEditParams.Instruction(const Value: string): TEditParams; 167 | begin 168 | Result := TEditParams(Add('instruction', Value)); 169 | end; 170 | 171 | function TEditParams.Temperature(const Value: Single): TEditParams; 172 | begin 173 | Result := TEditParams(Add('temperature', Value)); 174 | end; 175 | 176 | function TEditParams.TopP(const Value: Single): TEditParams; 177 | begin 178 | Result := TEditParams(Add('top_p', Value)); 179 | end; 180 | 181 | end. 182 | 183 | -------------------------------------------------------------------------------- /Samples/Chat/Chat.Main.pas: -------------------------------------------------------------------------------- 1 | unit Chat.Main; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, REST.Types, 8 | Data.Bind.Components, Data.Bind.ObjectScope, REST.Client, OpenAI, FMX.Styles, 9 | OpenAI.Component.Chat, FMX.Memo.Types, FMX.StdCtrls, FMX.Controls.Presentation, 10 | FMX.ScrollBox, FMX.Memo, OpenAI.Component.Functions, FMX.Layouts, FMX.ListBox, 11 | OpenAI.Types, OpenAI.Component.ChatHistory; 12 | 13 | type 14 | TFormChat = class(TForm) 15 | OpenAIClient1: TOpenAIClient; 16 | OpenAIChat1: TOpenAIChat; 17 | MemoMessages: TMemo; 18 | MemoMessage: TMemo; 19 | ButtonSend: TButton; 20 | ButtonStreamSend: TButton; 21 | AniIndicatorBusy: TAniIndicator; 22 | OpenAIChatFunctions1: TOpenAIChatFunctions; 23 | ListBox1: TListBox; 24 | ButtonAttach: TButton; 25 | ButtonRemoveAttach: TButton; 26 | OpenDialogImg: TOpenDialog; 27 | OpenAIChatHistory1: TOpenAIChatHistory; 28 | procedure FormCreate(Sender: TObject); 29 | procedure OpenAIChat1Chat(Sender: TObject; Event: TChat); 30 | procedure OpenAIChat1Error(Sender: TObject; Error: Exception); 31 | procedure ButtonSendClick(Sender: TObject); 32 | procedure ButtonStreamSendClick(Sender: TObject); 33 | procedure OpenAIChat1BeginWork(Sender: TObject); 34 | procedure OpenAIChat1ChatDelta(Sender: TObject; Event: TChat; IsDone: Boolean); 35 | procedure OpenAIChat1EndWork(Sender: TObject); 36 | procedure FuncGetCurrentWeather(Sender: TObject; const Args: string; out Result: string); 37 | procedure ButtonAttachClick(Sender: TObject); 38 | procedure OpenAIChatFunctions1Items1FunctionExecute(Sender: TObject; const Args: string; out Result: string); 39 | private 40 | { Private declarations } 41 | public 42 | { Public declarations } 43 | end; 44 | 45 | var 46 | FormChat: TFormChat; 47 | 48 | implementation 49 | 50 | uses 51 | System.JSON, System.IOUtils, OpenAI.Chat, OpenAI.Utils.Base64; 52 | 53 | {$R *.fmx} 54 | 55 | procedure TFormChat.ButtonAttachClick(Sender: TObject); 56 | begin 57 | if OpenDialogImg.Execute then 58 | begin 59 | var Item := TListBoxItem.Create(ListBox1); 60 | Item.Text := TPath.GetFileName(OpenDialogImg.FileName); 61 | Item.TagString := OpenDialogImg.FileName; 62 | Item.ItemData.Bitmap.LoadThumbnailFromFile(OpenDialogImg.FileName, 75, 75); 63 | ListBox1.AddObject(Item); 64 | end; 65 | end; 66 | 67 | procedure TFormChat.ButtonSendClick(Sender: TObject); 68 | begin 69 | MemoMessages.Lines.Add('User: ' + MemoMessage.Text); 70 | MemoMessages.Lines.Add(''); 71 | OpenAIChat1.Stream := False; 72 | var Content: TArray; 73 | 74 | if not MemoMessage.Text.IsEmpty then 75 | Content := Content + [TMessageContent.CreateText(MemoMessage.Text)]; 76 | 77 | for var i := 0 to ListBox1.Count - 1 do 78 | Content := Content + [TMessageContent.CreateImage(FileToBase64(ListBox1.ListItems[i].TagString))]; 79 | 80 | if Length(Content) <= 0 then 81 | Exit; 82 | 83 | OpenAIChat1.Send([TChatMessageBuild.User(Content)]); 84 | MemoMessage.Text := ''; 85 | end; 86 | 87 | procedure TFormChat.ButtonStreamSendClick(Sender: TObject); 88 | begin 89 | MemoMessages.Lines.Add('User: ' + MemoMessage.Text); 90 | OpenAIChat1.Stream := True; 91 | OpenAIChat1.Send([TChatMessageBuild.User(MemoMessage.Text)]); 92 | MemoMessages.Lines.Add('Assistant: '); 93 | end; 94 | 95 | procedure TFormChat.FormCreate(Sender: TObject); 96 | begin 97 | OpenAIClient1.Token := {$INCLUDE token.txt}; //'sk-...' 98 | 99 | OpenAIClient1.API.CustomHeaders := OpenAIClient1.API.CustomHeaders + [THeaderItem.Create('OpenAI-Beta', 'assistants=v2')]; 100 | end; 101 | 102 | procedure TFormChat.OpenAIChat1BeginWork(Sender: TObject); 103 | begin 104 | AniIndicatorBusy.Visible := True; 105 | end; 106 | 107 | procedure TFormChat.OpenAIChat1Chat(Sender: TObject; Event: TChat); 108 | begin 109 | MemoMessages.Lines.Add('Assistant: ' + Event.Choices[0].Message.Content); 110 | MemoMessages.Lines.Add(''); 111 | end; 112 | 113 | procedure TFormChat.OpenAIChat1ChatDelta(Sender: TObject; Event: TChat; IsDone: Boolean); 114 | begin 115 | if Assigned(Event) then 116 | begin 117 | if Event.Choices[0].FinishReason = TFinishReason.FunctionCall then 118 | begin 119 | MemoMessages.Lines.Add('Call function ' + Event.Choices[0].Delta.FunctionCall.Name); 120 | end 121 | else 122 | MemoMessages.Text := MemoMessages.Text + Event.Choices[0].Delta.Content; 123 | end; 124 | if IsDone then 125 | MemoMessages.Lines.Add(''); 126 | end; 127 | 128 | procedure TFormChat.OpenAIChat1EndWork(Sender: TObject); 129 | begin 130 | AniIndicatorBusy.Visible := False; 131 | end; 132 | 133 | procedure TFormChat.OpenAIChat1Error(Sender: TObject; Error: Exception); 134 | begin 135 | MemoMessages.Lines.Add('Error: ' + Error.Message); 136 | MemoMessages.Lines.Add(''); 137 | end; 138 | 139 | procedure TFormChat.OpenAIChatFunctions1Items1FunctionExecute(Sender: TObject; const Args: string; out Result: string); 140 | begin 141 | Result := ''' 142 | { 143 | "name": "ROOT", 144 | "user_name": "HemulGM", 145 | "ram": "32Gb", 146 | "cpu": "Intel(R) Core(TM) i7-9700KF CPU @ 3.60GHz" 147 | } 148 | '''; 149 | end; 150 | 151 | procedure TFormChat.FuncGetCurrentWeather(Sender: TObject; const Args: string; out Result: string); 152 | begin 153 | Result := ''; 154 | var Location := ''; 155 | var UnitKind := ''; 156 | 157 | // Parse arguments 158 | try 159 | var JSON := TJSONObject.ParseJSONValue(Args) as TJSONObject; 160 | if Assigned(JSON) then 161 | try 162 | Location := JSON.GetValue('location', ''); 163 | UnitKind := JSON.GetValue('unit', ''); 164 | finally 165 | JSON.Free; 166 | end; 167 | except 168 | Location := ''; 169 | end; 170 | 171 | // Invalid arguments 172 | if Location.IsEmpty then 173 | Exit; 174 | 175 | // Generate response 176 | var JSON := TJSONObject.Create; 177 | try 178 | JSON.AddPair('location', Location); 179 | JSON.AddPair('unit', UnitKind); 180 | 181 | JSON.AddPair('temperature', TJSONNumber.Create(14)); 182 | JSON.AddPair('forecast', TJSONArray.Create('sunny', 'windy')); 183 | 184 | Result := JSON.ToJSON; 185 | finally 186 | JSON.Free; 187 | end; 188 | end; 189 | 190 | initialization 191 | ReportMemoryLeaksOnShutdown := True; 192 | 193 | end. 194 | 195 | -------------------------------------------------------------------------------- /OpenAI.Utils.ChatHistory.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Utils.ChatHistory; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Generics.Collections, OpenAI.Chat; 7 | 8 | type 9 | TOnCalcTokens = procedure(Sender: TObject; const Content: string; var TokenCount: Int64) of object; 10 | 11 | /// 12 | /// This class is used to store chat history. 13 | /// It can automatically delete previous messages if the total message size exceeds the specified number of tokens. 14 | ///
15 | /// Use the ToArray method to pass the history to the Chat.Create request parameters 16 | ///
17 | TChatHistory = class(TList) 18 | private 19 | FAutoTrim: Boolean; 20 | FMaxTokensForQuery: Int64; 21 | FMaxTokensOfModel: Int64; 22 | FOnCalcContentTokens: TOnCalcTokens; 23 | procedure SetAutoTrim(const Value: Boolean); 24 | procedure SetMaxTokensForQuery(const Value: Int64); 25 | procedure SetMaxTokensOfModel(const Value: Int64); 26 | procedure SetOnCalcContentTokens(const Value: TOnCalcTokens); 27 | protected 28 | procedure Notify(const Item: TChatMessageBuild; Action: TCollectionNotification); override; 29 | public 30 | procedure New(Message: TChatMessageBuild); overload; 31 | procedure New(Role: TMessageRole; const Content, Tag: string); overload; 32 | procedure NewFunc(const FuncName, FuncResult, Tag: string); 33 | procedure NewTool(const Content, ToolCallId, Name, Tag: string); 34 | procedure NewAsistantFunc(const FuncName, Args, Tag: string); 35 | procedure NewAsistantTool(const Content: string; const ToolCalls: TArray; const Tag: string); 36 | procedure SetContentByTag(const Tag, Text: string); 37 | function TextLength: Int64; 38 | property AutoTrim: Boolean read FAutoTrim write SetAutoTrim; 39 | property MaxTokensForQuery: Int64 read FMaxTokensForQuery write SetMaxTokensForQuery; 40 | property MaxTokensOfModel: Int64 read FMaxTokensOfModel write SetMaxTokensOfModel; 41 | property OnCalcContentTokens: TOnCalcTokens read FOnCalcContentTokens write SetOnCalcContentTokens; 42 | procedure DeleteByTag(const Tag: string); 43 | constructor Create; 44 | end; 45 | 46 | const 47 | DEFULT_MAX_TOKENS = 1024; 48 | DEFULT_MODEL_TOKENS_LIMIT = 4096; 49 | 50 | implementation 51 | 52 | { TChatHistory } 53 | 54 | constructor TChatHistory.Create; 55 | begin 56 | inherited; 57 | FAutoTrim := False; 58 | FMaxTokensForQuery := DEFULT_MAX_TOKENS; 59 | FMaxTokensOfModel := DEFULT_MODEL_TOKENS_LIMIT; 60 | end; 61 | 62 | procedure TChatHistory.DeleteByTag(const Tag: string); 63 | var 64 | i: Integer; 65 | begin 66 | for i := 0 to Count - 1 do 67 | if Items[i].Tag = Tag then 68 | begin 69 | Delete(i); 70 | Exit; 71 | end; 72 | end; 73 | 74 | procedure TChatHistory.SetContentByTag(const Tag, Text: string); 75 | var 76 | i: Integer; 77 | Item: TChatMessageBuild; 78 | begin 79 | for i := 0 to Count - 1 do 80 | if Items[i].Tag = Tag then 81 | begin 82 | Item := Items[i]; 83 | Item.Content := Text; 84 | Items[i] := Item; 85 | Exit; 86 | end; 87 | end; 88 | 89 | procedure TChatHistory.New(Role: TMessageRole; const Content, Tag: string); 90 | var 91 | Item: TChatMessageBuild; 92 | begin 93 | Item := TChatMessageBuild.Create(Role, Content); 94 | Item.Tag := Tag; 95 | Add(Item); 96 | end; 97 | 98 | procedure TChatHistory.New(Message: TChatMessageBuild); 99 | begin 100 | Add(Message); 101 | end; 102 | 103 | procedure TChatHistory.NewAsistantFunc(const FuncName, Args, Tag: string); 104 | var 105 | Item: TChatMessageBuild; 106 | begin 107 | Item := TChatMessageBuild.AssistantFunc(FuncName, Args); 108 | Item.Tag := Tag; 109 | Add(Item); 110 | end; 111 | 112 | procedure TChatHistory.NewAsistantTool(const Content: string; const ToolCalls: TArray; const Tag: string); 113 | var 114 | Item: TChatMessageBuild; 115 | begin 116 | Item := TChatMessageBuild.AssistantTool(Content, ToolCalls); 117 | Item.Tag := Tag; 118 | Add(Item); 119 | end; 120 | 121 | procedure TChatHistory.NewFunc(const FuncName, FuncResult, Tag: string); 122 | var 123 | Item: TChatMessageBuild; 124 | begin 125 | Item := TChatMessageBuild.Func(FuncResult, FuncName); 126 | Item.Tag := Tag; 127 | Add(Item); 128 | end; 129 | 130 | procedure TChatHistory.NewTool(const Content, ToolCallId, Name, Tag: string); 131 | var 132 | Item: TChatMessageBuild; 133 | begin 134 | Item := TChatMessageBuild.Tool(Content, ToolCallId, Name); 135 | Item.Tag := Tag; 136 | Add(Item); 137 | end; 138 | 139 | procedure TChatHistory.Notify(const Item: TChatMessageBuild; Action: TCollectionNotification); 140 | var 141 | LastItem: TChatMessageBuild; 142 | NeedLen: Int64; 143 | begin 144 | inherited; 145 | if FAutoTrim and (Action = TCollectionNotification.cnAdded) then 146 | begin 147 | while TextLength + FMaxTokensForQuery > FMaxTokensOfModel do 148 | if Count = 1 then 149 | begin 150 | LastItem := Items[0]; 151 | NeedLen := LastItem.Content.Length - FMaxTokensForQuery; 152 | LastItem.Content := LastItem.Content.Substring(LastItem.Content.Length - NeedLen, NeedLen); 153 | Items[0] := LastItem; 154 | end 155 | else 156 | Delete(0); 157 | end; 158 | end; 159 | 160 | procedure TChatHistory.SetAutoTrim(const Value: Boolean); 161 | begin 162 | FAutoTrim := Value; 163 | end; 164 | 165 | procedure TChatHistory.SetMaxTokensForQuery(const Value: Int64); 166 | begin 167 | FMaxTokensForQuery := Value; 168 | if FMaxTokensForQuery <= 0 then 169 | FMaxTokensForQuery := DEFULT_MAX_TOKENS; 170 | end; 171 | 172 | procedure TChatHistory.SetMaxTokensOfModel(const Value: Int64); 173 | begin 174 | FMaxTokensOfModel := Value; 175 | if FMaxTokensOfModel <= 0 then 176 | FMaxTokensOfModel := DEFULT_MODEL_TOKENS_LIMIT; 177 | end; 178 | 179 | procedure TChatHistory.SetOnCalcContentTokens(const Value: TOnCalcTokens); 180 | begin 181 | FOnCalcContentTokens := Value; 182 | end; 183 | 184 | function TChatHistory.TextLength: Int64; 185 | var 186 | ItemTokenCount: Int64; 187 | Item: TChatMessageBuild; 188 | begin 189 | Result := 0; 190 | if Assigned(FOnCalcContentTokens) then 191 | for Item in Self do 192 | begin 193 | ItemTokenCount := 0; 194 | FOnCalcContentTokens(Self, Item.Content, ItemTokenCount); 195 | Inc(Result, ItemTokenCount); 196 | end 197 | else 198 | begin 199 | for Item in Self do 200 | Inc(Result, Item.Content.Length); 201 | end; 202 | end; 203 | 204 | end. 205 | 206 | -------------------------------------------------------------------------------- /OpenAI.Component.Functions.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Component.Functions; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.SysUtils, OpenAI.Chat.Functions; 7 | 8 | type 9 | TChatFunction = OpenAI.Chat.Functions.TChatFunction; 10 | 11 | EFunctionNotImplemented = class(Exception); 12 | 13 | EFunctionNotFound = class(Exception); 14 | 15 | TOnFunctionExecute = procedure(Sender: TObject; const Args: string; out Result: string) of object; 16 | 17 | TChatFunctionContainer = class(TChatFunction, IChatFunction) 18 | private 19 | FName: string; 20 | FParameters: string; 21 | FDescription: string; 22 | FStrict: Boolean; 23 | FFunction: TOnFunctionExecute; 24 | procedure SetDescription(const Value: string); 25 | procedure SetName(const Value: string); 26 | procedure SetParameters(const Value: string); 27 | procedure SetStrict(const Value: Boolean); 28 | protected 29 | function GetStrict: Boolean; override; 30 | function GetDescription: string; override; 31 | function GetName: string; override; 32 | function GetParameters: string; override; 33 | public 34 | function Execute(const Args: string): string; override; 35 | property Description: string read GetDescription write SetDescription; 36 | property Name: string read GetName write SetName; 37 | property Parameters: string read GetParameters write SetParameters; 38 | property Strict: Boolean read GetStrict write SetStrict; 39 | end; 40 | 41 | TFunctionCollectionItem = class(TCollectionItem) 42 | private 43 | FFunction: IChatFunction; 44 | function GetDescription: string; 45 | function GetName: string; 46 | function GetParameters: string; 47 | procedure SetDescription(const Value: string); 48 | procedure SetName(const Value: string); 49 | procedure SetParameters(const Value: string); 50 | procedure SetOnFunctionExecute(const Value: TOnFunctionExecute); 51 | function GetOnFunctionExecute: TOnFunctionExecute; 52 | function GetStrict: Boolean; 53 | procedure SetStrict(const Value: Boolean); 54 | protected 55 | function GetDisplayName: string; override; 56 | published 57 | property Description: string read GetDescription write SetDescription; 58 | property Name: string read GetName write SetName; 59 | property Parameters: string read GetParameters write SetParameters; 60 | property Strict: Boolean read GetStrict write SetStrict; 61 | property OnFunctionExecute: TOnFunctionExecute read GetOnFunctionExecute write SetOnFunctionExecute; 62 | constructor Create(Collection: TCollection); override; 63 | destructor Destroy; override; 64 | end; 65 | 66 | TFunctionCollection = class(TOwnedCollection) 67 | end; 68 | 69 | TOpenAIChatFunctions = class(TComponent) 70 | private 71 | FItems: TFunctionCollection; 72 | public 73 | constructor Create(AOwner: TComponent); override; 74 | destructor Destroy; override; 75 | function GetList: TArray; 76 | function Call(const FuncName, FuncArgs: string): string; 77 | published 78 | property Items: TFunctionCollection read FItems write FItems; 79 | end; 80 | 81 | implementation 82 | 83 | { TOpenAIChatFunctions } 84 | 85 | function TOpenAIChatFunctions.Call(const FuncName, FuncArgs: string): string; 86 | begin 87 | for var Item in FItems do 88 | if TFunctionCollectionItem(Item).Name = FuncName then 89 | Exit(TFunctionCollectionItem(Item).FFunction.Execute(FuncArgs)); 90 | raise EFunctionNotFound.CreateFmt('Function "%s" not found', [FuncName]); 91 | end; 92 | 93 | constructor TOpenAIChatFunctions.Create(AOwner: TComponent); 94 | begin 95 | inherited; 96 | FItems := TFunctionCollection.Create(Self, TFunctionCollectionItem); 97 | end; 98 | 99 | destructor TOpenAIChatFunctions.Destroy; 100 | begin 101 | FItems.Free; 102 | inherited; 103 | end; 104 | 105 | function TOpenAIChatFunctions.GetList: TArray; 106 | begin 107 | SetLength(Result, FItems.Count); 108 | var i: Integer := 0; 109 | for var Item in FItems do 110 | begin 111 | Result[i] := TFunctionCollectionItem(Item).FFunction; 112 | Inc(i); 113 | end; 114 | end; 115 | 116 | { TFunctionCollectionItem } 117 | 118 | constructor TFunctionCollectionItem.Create(Collection: TCollection); 119 | begin 120 | inherited; 121 | FFunction := TChatFunctionContainer.Create; 122 | end; 123 | 124 | destructor TFunctionCollectionItem.Destroy; 125 | begin 126 | FFunction := nil; 127 | inherited; 128 | end; 129 | 130 | function TFunctionCollectionItem.GetDescription: string; 131 | begin 132 | Result := FFunction.Description; 133 | end; 134 | 135 | function TFunctionCollectionItem.GetDisplayName: string; 136 | begin 137 | Result := FFunction.Name; 138 | end; 139 | 140 | function TFunctionCollectionItem.GetName: string; 141 | begin 142 | Result := FFunction.Name; 143 | end; 144 | 145 | function TFunctionCollectionItem.GetOnFunctionExecute: TOnFunctionExecute; 146 | begin 147 | Result := TChatFunctionContainer(FFunction).FFunction; 148 | end; 149 | 150 | function TFunctionCollectionItem.GetParameters: string; 151 | begin 152 | Result := FFunction.Parameters; 153 | end; 154 | 155 | function TFunctionCollectionItem.GetStrict: Boolean; 156 | begin 157 | Result := TChatFunctionContainer(FFunction).FStrict; 158 | end; 159 | 160 | procedure TFunctionCollectionItem.SetDescription(const Value: string); 161 | begin 162 | TChatFunctionContainer(FFunction).FDescription := Value; 163 | end; 164 | 165 | procedure TFunctionCollectionItem.SetName(const Value: string); 166 | begin 167 | TChatFunctionContainer(FFunction).FName := Value; 168 | end; 169 | 170 | procedure TFunctionCollectionItem.SetOnFunctionExecute(const Value: TOnFunctionExecute); 171 | begin 172 | TChatFunctionContainer(FFunction).FFunction := Value; 173 | end; 174 | 175 | procedure TFunctionCollectionItem.SetParameters(const Value: string); 176 | begin 177 | TChatFunctionContainer(FFunction).FParameters := Value; 178 | end; 179 | 180 | procedure TFunctionCollectionItem.SetStrict(const Value: Boolean); 181 | begin 182 | TChatFunctionContainer(FFunction).FStrict := Value; 183 | end; 184 | 185 | { TChatFunctionContainer } 186 | 187 | function TChatFunctionContainer.Execute(const Args: string): string; 188 | begin 189 | if Assigned(FFunction) then 190 | FFunction(Self, Args, Result) 191 | else 192 | raise EFunctionNotImplemented.Create('Function not implemented'); 193 | end; 194 | 195 | function TChatFunctionContainer.GetDescription: string; 196 | begin 197 | Result := FDescription; 198 | end; 199 | 200 | function TChatFunctionContainer.GetName: string; 201 | begin 202 | Result := FName; 203 | end; 204 | 205 | function TChatFunctionContainer.GetParameters: string; 206 | begin 207 | Result := FParameters; 208 | end; 209 | 210 | function TChatFunctionContainer.GetStrict: Boolean; 211 | begin 212 | Result := FStrict; 213 | end; 214 | 215 | procedure TChatFunctionContainer.SetDescription(const Value: string); 216 | begin 217 | FDescription := Value; 218 | end; 219 | 220 | procedure TChatFunctionContainer.SetName(const Value: string); 221 | begin 222 | FName := Value; 223 | end; 224 | 225 | procedure TChatFunctionContainer.SetParameters(const Value: string); 226 | begin 227 | FParameters := Value; 228 | end; 229 | 230 | procedure TChatFunctionContainer.SetStrict(const Value: Boolean); 231 | begin 232 | FStrict := Value; 233 | end; 234 | 235 | end. 236 | 237 | -------------------------------------------------------------------------------- /OpenAI.Files.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Files; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.SysUtils, System.Net.Mime, OpenAI.API.Params, 7 | OpenAI.API, OpenAI.Types; 8 | 9 | {$SCOPEDENUMS ON} 10 | 11 | type 12 | TFileCreatePurpose = (FineTune, Answers, Search, Classifications); 13 | 14 | TFileCreatePurposeHelper = record helper for TFileCreatePurpose 15 | function ToString: string; 16 | end; 17 | 18 | TFileUploadParams = class(TMultipartFormData) 19 | /// 20 | /// Name of the JSON Lines file to be uploaded. 21 | /// If the purpose is set to "fine-tune", the file will be used for fine-tuning. 22 | /// 23 | function &File(const FileName: TFileName): TFileUploadParams; overload; 24 | /// 25 | /// Name of the JSON Lines file to be uploaded. 26 | /// If the purpose is set to "fine-tune", the file will be used for fine-tuning. 27 | /// 28 | function &File(const Stream: TStream; const FileName: TFileName): TFileUploadParams; overload; 29 | /// 30 | /// The intended purpose of the uploaded documents. 31 | /// Use "fine-tune" for fine-tuning. This allows us to validate the format of the uploaded file. 32 | /// Variants: ['fine-tune', 'answers', 'search', 'classifications'] 33 | /// 34 | function Purpose(const Value: string): TFileUploadParams; overload; 35 | /// 36 | /// The intended purpose of the uploaded documents. 37 | /// Use "fine-tune" for Fine-tuning. This allows us to validate the format of the uploaded file. 38 | /// 39 | function Purpose(const Value: TFileCreatePurpose): TFileUploadParams; overload; 40 | constructor Create; reintroduce; 41 | end; 42 | 43 | /// 44 | /// The File object represents a document that has been uploaded to OpenAI. 45 | /// 46 | TFile = class 47 | private 48 | FBytes: Int64; 49 | FCreated_at: Int64; 50 | FFilename: TFileName; 51 | FId: string; 52 | FObject: string; 53 | FPurpose: string; 54 | FStatus: string; 55 | FStatus_details: string; 56 | public 57 | /// 58 | /// The file identifier, which can be referenced in the API endpoints. 59 | /// 60 | property Id: string read FId write FId; 61 | /// 62 | /// The object type, which is always "file". 63 | /// 64 | property &Object: string read FObject write FObject; 65 | /// 66 | /// The size of the file in bytes. 67 | /// 68 | property Bytes: Int64 read FBytes write FBytes; 69 | /// 70 | /// The Unix timestamp (in seconds) for when the file was created. 71 | /// 72 | property CreatedAt: Int64 read FCreated_at write FCreated_at; 73 | /// 74 | /// The name of the file. 75 | /// 76 | property FileName: TFileName read FFilename write FFilename; 77 | /// 78 | /// The intended purpose of the file. Currently, only "fine-tune" is supported. 79 | /// 80 | property Purpose: string read FPurpose write FPurpose; 81 | /// 82 | /// DEPRECATED 83 | /// The current status of the file, which can be either uploaded, processed, pending, error, deleting or deleted. 84 | /// 85 | property Status: string read FStatus write FStatus; 86 | /// 87 | /// DEPRECATED 88 | /// Additional details about the status of the file. If the file is in the error state, 89 | /// this will include a message describing the error. 90 | /// 91 | property StatusDetails: string read FStatus_details write FStatus_details; 92 | end; 93 | 94 | TFiles = class 95 | private 96 | FData: TArray; 97 | FObject: string; 98 | public 99 | property Data: TArray read FData write FData; 100 | property &Object: string read FObject write FObject; 101 | destructor Destroy; override; 102 | end; 103 | 104 | TFilesRoute = class(TOpenAIAPIRoute) 105 | public 106 | /// 107 | /// Returns a list of files that belong to the user's organization. 108 | /// 109 | function List: TFiles; 110 | /// 111 | /// Upload a file that contains document(s) to be used across various endpoints/features. 112 | /// Currently, the size of all the files uploaded by one organization can be up to 1 GB. 113 | /// Please contact us if you need to increase the storage limit. 114 | /// 115 | function Upload(ParamProc: TProc): TFile; 116 | /// 117 | /// Delete a file. 118 | /// 119 | function Delete(const FileId: string = ''): TDeletionStatus; 120 | /// 121 | /// Returns information about a specific file. 122 | /// 123 | function Retrieve(const FileId: string = ''): TFile; 124 | /// 125 | /// Returns the contents of the specified file 126 | /// 127 | procedure Download(const FileId: string; Stream: TStream); 128 | end; 129 | 130 | implementation 131 | 132 | { TFilesRoute } 133 | 134 | function TFilesRoute.Upload(ParamProc: TProc): TFile; 135 | begin 136 | Result := API.PostForm('files', ParamProc); 137 | end; 138 | 139 | function TFilesRoute.Delete(const FileId: string): TDeletionStatus; 140 | begin 141 | Result := API.Delete('files/' + FileId); 142 | end; 143 | 144 | procedure TFilesRoute.Download(const FileId: string; Stream: TStream); 145 | begin 146 | API.GetFile('files/' + FileId + '/content', Stream); 147 | end; 148 | 149 | function TFilesRoute.List: TFiles; 150 | begin 151 | Result := API.Get('files'); 152 | end; 153 | 154 | function TFilesRoute.Retrieve(const FileId: string): TFile; 155 | begin 156 | Result := API.Get('files/' + FileId); 157 | end; 158 | 159 | { TFiles } 160 | 161 | destructor TFiles.Destroy; 162 | var 163 | Item: TFile; 164 | begin 165 | for Item in FData do 166 | if Assigned(Item) then 167 | Item.Free; 168 | inherited; 169 | end; 170 | 171 | { TFileUploadParams } 172 | 173 | function TFileUploadParams.&File(const FileName: TFileName): TFileUploadParams; 174 | begin 175 | AddFile('file', FileName); 176 | Result := Self; 177 | end; 178 | 179 | constructor TFileUploadParams.Create; 180 | begin 181 | inherited Create(True); 182 | end; 183 | 184 | function TFileUploadParams.&File(const Stream: TStream; const FileName: TFileName): TFileUploadParams; 185 | begin 186 | {$IF CompilerVersion <= 35} 187 | AddStream('file', Stream, FileName); 188 | {$ELSE} 189 | AddStream('file', Stream, False, FileName); 190 | {$ENDIF} 191 | Result := Self; 192 | end; 193 | 194 | function TFileUploadParams.Purpose(const Value: TFileCreatePurpose): TFileUploadParams; 195 | begin 196 | Result := Purpose(Value.ToString); 197 | end; 198 | 199 | function TFileUploadParams.Purpose(const Value: string): TFileUploadParams; 200 | begin 201 | AddField('purpose', Value); 202 | Result := Self; 203 | end; 204 | 205 | { TFileCreatePurposeHelper } 206 | 207 | function TFileCreatePurposeHelper.ToString: string; 208 | begin 209 | case Self of 210 | TFileCreatePurpose.FineTune: 211 | Result := 'fine-tune'; 212 | TFileCreatePurpose.Answers: 213 | Result := 'answers'; 214 | TFileCreatePurpose.Search: 215 | Result := 'search'; 216 | TFileCreatePurpose.Classifications: 217 | Result := 'classifications'; 218 | end; 219 | end; 220 | 221 | end. 222 | 223 | -------------------------------------------------------------------------------- /OpenAI.Embeddings.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Embeddings; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API.Params, OpenAI.API; 7 | 8 | type 9 | TEncodingFormat = (Float, Base64); 10 | 11 | TEncodingFormatHelper = record helper for TEncodingFormat 12 | function ToString: string; 13 | class function FromString(const Value: string): TEncodingFormat; static; 14 | end; 15 | 16 | TEmbeddingParams = class(TJSONParam) 17 | /// 18 | /// ID of the model to use. You can use the List models API to see all of your available models, 19 | /// or see our Model overview for descriptions of them. 20 | /// 21 | function Model(const Value: string): TEmbeddingParams; 22 | /// 23 | /// Input text to embed, encoded as a string or array of tokens. 24 | /// To embed multiple inputs in a single request, pass an array of strings or array of token arrays. 25 | /// The input must not exceed the max input tokens for the model (8192 tokens for text-embedding-ada-002), 26 | /// cannot be an empty string, and any array must be 2048 dimensions or less. 27 | /// 28 | function Input(const Value: string): TEmbeddingParams; overload; 29 | /// 30 | /// Input text to embed, encoded as a string or array of tokens. 31 | /// To embed multiple inputs in a single request, pass an array of strings or array of token arrays. 32 | /// The input must not exceed the max input tokens for the model (8192 tokens for text-embedding-ada-002), 33 | /// cannot be an empty string, and any array must be 2048 dimensions or less. 34 | /// 35 | function Input(const Value: TArray): TEmbeddingParams; overload; 36 | /// 37 | /// Input text to embed, encoded as a string or array of tokens. 38 | /// To embed multiple inputs in a single request, pass an array of strings or array of token arrays. 39 | /// The input must not exceed the max input tokens for the model (8192 tokens for text-embedding-ada-002), 40 | /// cannot be an empty string, and any array must be 2048 dimensions or less. 41 | /// 42 | function Input(const Value: TArray): TEmbeddingParams; overload; 43 | /// 44 | /// Input text to embed, encoded as a string or array of tokens. 45 | /// To embed multiple inputs in a single request, pass an array of strings or array of token arrays. 46 | /// The input must not exceed the max input tokens for the model (8192 tokens for text-embedding-ada-002), 47 | /// cannot be an empty string, and any array must be 2048 dimensions or less. 48 | /// 49 | function Input(const Value: TArray>): TEmbeddingParams; overload; 50 | /// 51 | /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. 52 | /// 53 | function User(const Value: string): TEmbeddingParams; overload; 54 | /// 55 | /// The format to return the embeddings in. Can be either float or base64. 56 | /// 57 | function EncodingFormat(const Value: TEncodingFormat): TEmbeddingParams; overload; 58 | /// 59 | /// The number of dimensions the resulting output embeddings should have. 60 | /// Only supported in text-embedding-3 and later models. 61 | /// 62 | function Dimensions(const Value: Int64): TEmbeddingParams; overload; 63 | end; 64 | 65 | TEmbeddingUsage = class 66 | private 67 | FPrompt_tokens: Int64; 68 | FTotal_tokens: Int64; 69 | public 70 | property PromptTokens: Int64 read FPrompt_tokens write FPrompt_tokens; 71 | property TotalTokens: Int64 read FTotal_tokens write FTotal_tokens; 72 | end; 73 | 74 | /// 75 | /// Represents an embedding vector returned by embedding endpoint. 76 | /// 77 | TEmbeddingData = class 78 | private 79 | FIndex: Int64; 80 | FObject: string; 81 | FEmbedding: TArray; 82 | public 83 | /// 84 | /// The index of the embedding in the list of embeddings. 85 | /// 86 | property Index: Int64 read FIndex write FIndex; 87 | /// 88 | /// The object type, which is always "embedding". 89 | /// 90 | property &Object: string read FObject write FObject; 91 | /// 92 | /// The embedding vector, which is a list of floats. 93 | /// The length of vector depends on the model as listed in the embedding guide. 94 | /// 95 | /// https://platform.openai.com/docs/guides/embeddings 96 | property Embedding: TArray read FEmbedding write FEmbedding; 97 | end; 98 | 99 | TEmbeddings = class 100 | private 101 | FData: TArray; 102 | FObject: string; 103 | FUsage: TEmbeddingUsage; 104 | FModel: string; 105 | public 106 | property &Object: string read FObject write FObject; 107 | property Data: TArray read FData write FData; 108 | property Usage: TEmbeddingUsage read FUsage write FUsage; 109 | property Model: string read FModel write FModel; 110 | destructor Destroy; override; 111 | end; 112 | 113 | TEmbeddingsRoute = class(TOpenAIAPIRoute) 114 | public 115 | /// 116 | /// Creates an embedding vector representing the input text. 117 | /// 118 | function Create(ParamProc: TProc): TEmbeddings; 119 | end; 120 | 121 | implementation 122 | 123 | { TEmbeddingsRoute } 124 | 125 | function TEmbeddingsRoute.Create(ParamProc: TProc): TEmbeddings; 126 | begin 127 | Result := API.Post('embeddings', ParamProc); 128 | end; 129 | 130 | { TEmbeddings } 131 | 132 | destructor TEmbeddings.Destroy; 133 | var 134 | Item: TEmbeddingData; 135 | begin 136 | FUsage.Free; 137 | for Item in FData do 138 | Item.Free; 139 | inherited; 140 | end; 141 | 142 | { TEmbeddingParams } 143 | 144 | function TEmbeddingParams.Dimensions(const Value: Int64): TEmbeddingParams; 145 | begin 146 | Result := TEmbeddingParams(Add('dimensions', Value)); 147 | end; 148 | 149 | function TEmbeddingParams.EncodingFormat(const Value: TEncodingFormat): TEmbeddingParams; 150 | begin 151 | Result := TEmbeddingParams(Add('encoding_format', Value.ToString)); 152 | end; 153 | 154 | function TEmbeddingParams.Input(const Value: TArray): TEmbeddingParams; 155 | begin 156 | Result := TEmbeddingParams(Add('input', Value)); 157 | end; 158 | 159 | function TEmbeddingParams.Input(const Value: TArray): TEmbeddingParams; 160 | begin 161 | Result := TEmbeddingParams(Add('input', Value)); 162 | end; 163 | 164 | function TEmbeddingParams.Model(const Value: string): TEmbeddingParams; 165 | begin 166 | Result := TEmbeddingParams(Add('model', Value)); 167 | end; 168 | 169 | function TEmbeddingParams.Input(const Value: string): TEmbeddingParams; 170 | begin 171 | Result := TEmbeddingParams(Add('input', Value)); 172 | end; 173 | 174 | function TEmbeddingParams.User(const Value: string): TEmbeddingParams; 175 | begin 176 | Result := TEmbeddingParams(Add('user', Value)); 177 | end; 178 | 179 | function TEmbeddingParams.Input(const Value: TArray>): TEmbeddingParams; 180 | begin 181 | Result := TEmbeddingParams(Add('input', Value)); 182 | end; 183 | 184 | { TEncodingFormatHelper } 185 | 186 | class function TEncodingFormatHelper.FromString(const Value: string): TEncodingFormat; 187 | begin 188 | if Value = 'float' then 189 | Exit(TEncodingFormat.Float) 190 | else if Value = 'base64' then 191 | Exit(TEncodingFormat.Base64) 192 | else 193 | Exit(TEncodingFormat.Float); 194 | end; 195 | 196 | function TEncodingFormatHelper.ToString: string; 197 | begin 198 | case Self of 199 | Float: 200 | Exit('float'); 201 | Base64: 202 | Exit('base64'); 203 | else 204 | Result := 'float'; 205 | end; 206 | end; 207 | 208 | end. 209 | 210 | -------------------------------------------------------------------------------- /OpenAI.API.Params.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.API.Params; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.JSON, System.SysUtils, System.Types, System.RTTI, 7 | REST.JsonReflect, REST.Json.Interceptors, System.Generics.Collections; 8 | 9 | type 10 | TJSONInterceptorStringToString = class(TJSONInterceptor) 11 | constructor Create; reintroduce; 12 | protected 13 | RTTI: TRttiContext; 14 | end; 15 | 16 | type 17 | TJSONParam = class 18 | private 19 | FJSON: TJSONObject; 20 | procedure SetJSON(const Value: TJSONObject); 21 | function GetCount: Integer; 22 | public 23 | constructor Create; virtual; 24 | destructor Destroy; override; 25 | function Add(const Key: string; const Value: string): TJSONParam; overload; virtual; 26 | function Add(const Key: string; const Value: Integer): TJSONParam; overload; virtual; 27 | function Add(const Key: string; const Value: Extended): TJSONParam; overload; virtual; 28 | function Add(const Key: string; const Value: Boolean): TJSONParam; overload; virtual; 29 | function Add(const Key: string; const Value: TDateTime; Format: string): TJSONParam; overload; virtual; 30 | function Add(const Key: string; const Value: TJSONValue): TJSONParam; overload; virtual; 31 | function Add(const Key: string; const Value: TJSONParam): TJSONParam; overload; virtual; 32 | function Add(const Key: string; Value: TArray): TJSONParam; overload; virtual; 33 | function Add(const Key: string; Value: TArray): TJSONParam; overload; virtual; 34 | function Add(const Key: string; Value: TArray>): TJSONParam; overload; virtual; 35 | function Add(const Key: string; Value: TArray): TJSONParam; overload; virtual; 36 | function Add(const Key: string; Value: TArray): TJSONParam; overload; virtual; 37 | function Add(const Key: string; Value: TArray): TJSONParam; overload; virtual; 38 | function GetOrCreateObject(const Name: string): TJSONObject; 39 | function GetOrCreate(const Name: string): T; 40 | procedure Delete(const Key: string); virtual; 41 | procedure Clear; virtual; 42 | property Count: Integer read GetCount; 43 | property JSON: TJSONObject read FJSON write SetJSON; 44 | function ToJsonString(FreeObject: Boolean = False): string; virtual; 45 | function ToStringPairs: TArray>; 46 | function ToStream: TStringStream; 47 | end; 48 | 49 | const 50 | DATE_FORMAT = 'YYYY-MM-DD'; 51 | TIME_FORMAT = 'HH:NN:SS'; 52 | DATE_TIME_FORMAT = DATE_FORMAT + ' ' + TIME_FORMAT; 53 | 54 | implementation 55 | 56 | uses 57 | System.DateUtils; 58 | 59 | { TJSONInterceptorStringToString } 60 | 61 | constructor TJSONInterceptorStringToString.Create; 62 | begin 63 | ConverterType := ctString; 64 | ReverterType := rtString; 65 | end; 66 | 67 | { Fetch } 68 | 69 | type 70 | Fetch = class 71 | type 72 | TFetchProc = reference to procedure(const Element: T); 73 | public 74 | class procedure All(const Items: TArray; Proc: TFetchProc); 75 | end; 76 | 77 | { Fetch } 78 | 79 | class procedure Fetch.All(const Items: TArray; Proc: TFetchProc); 80 | var 81 | Item: T; 82 | begin 83 | for Item in Items do 84 | Proc(Item); 85 | end; 86 | 87 | { TJSONParam } 88 | 89 | function TJSONParam.Add(const Key, Value: string): TJSONParam; 90 | begin 91 | Delete(Key); 92 | FJSON.AddPair(Key, Value); 93 | Result := Self; 94 | end; 95 | 96 | function TJSONParam.Add(const Key: string; const Value: TJSONValue): TJSONParam; 97 | begin 98 | Delete(Key); 99 | FJSON.AddPair(Key, Value); 100 | Result := Self; 101 | end; 102 | 103 | function TJSONParam.Add(const Key: string; const Value: TJSONParam): TJSONParam; 104 | begin 105 | Add(Key, TJSONValue(Value.JSON.Clone)); 106 | Result := Self; 107 | end; 108 | 109 | function TJSONParam.Add(const Key: string; const Value: TDateTime; Format: string): TJSONParam; 110 | begin 111 | if Format.IsEmpty then 112 | Format := DATE_TIME_FORMAT; 113 | Add(Key, FormatDateTime(Format, System.DateUtils.TTimeZone.local.ToUniversalTime(Value))); 114 | Result := Self; 115 | end; 116 | 117 | function TJSONParam.Add(const Key: string; const Value: Boolean): TJSONParam; 118 | begin 119 | Add(Key, TJSONBool.Create(Value)); 120 | Result := Self; 121 | end; 122 | 123 | function TJSONParam.Add(const Key: string; const Value: Integer): TJSONParam; 124 | begin 125 | Add(Key, TJSONNumber.Create(Value)); 126 | Result := Self; 127 | end; 128 | 129 | function TJSONParam.Add(const Key: string; const Value: Extended): TJSONParam; 130 | begin 131 | Add(Key, TJSONNumber.Create(Value)); 132 | Result := Self; 133 | end; 134 | 135 | function TJSONParam.Add(const Key: string; Value: TArray): TJSONParam; 136 | var 137 | JArr: TJSONArray; 138 | begin 139 | JArr := TJSONArray.Create; 140 | Fetch.All(Value, JArr.AddElement); 141 | Add(Key, JArr); 142 | Result := Self; 143 | end; 144 | 145 | function TJSONParam.Add(const Key: string; Value: TArray): TJSONParam; 146 | var 147 | JArr: TJSONArray; 148 | Item: TJSONParam; 149 | begin 150 | JArr := TJSONArray.Create; 151 | for Item in Value do 152 | try 153 | JArr.AddElement(Item.JSON); 154 | Item.JSON := nil; 155 | finally 156 | Item.Free; 157 | end; 158 | 159 | Add(Key, JArr); 160 | Result := Self; 161 | end; 162 | 163 | function TJSONParam.Add(const Key: string; Value: TArray>): TJSONParam; 164 | var 165 | JArr, JArrInner: TJSONArray; 166 | InnerItem: Integer; 167 | Item: TArray; 168 | begin 169 | JArr := TJSONArray.Create; 170 | for Item in Value do 171 | begin 172 | JArrInner := TJSONArray.Create; 173 | for InnerItem in Item do 174 | JArrInner.Add(InnerItem); 175 | JArr.Add(JArrInner); 176 | end; 177 | 178 | Add(Key, JArr); 179 | Result := Self; 180 | end; 181 | 182 | function TJSONParam.Add(const Key: string; Value: TArray): TJSONParam; 183 | var 184 | JArr: TJSONArray; 185 | Item: Extended; 186 | begin 187 | JArr := TJSONArray.Create; 188 | for Item in Value do 189 | JArr.Add(Item); 190 | 191 | Add(Key, JArr); 192 | Result := Self; 193 | end; 194 | 195 | function TJSONParam.Add(const Key: string; Value: TArray): TJSONParam; 196 | var 197 | JArr: TJSONArray; 198 | Item: Integer; 199 | begin 200 | JArr := TJSONArray.Create; 201 | for Item in Value do 202 | JArr.Add(Item); 203 | 204 | Add(Key, JArr); 205 | Result := Self; 206 | end; 207 | 208 | function TJSONParam.Add(const Key: string; Value: TArray): TJSONParam; 209 | var 210 | JArr: TJSONArray; 211 | Item: string; 212 | begin 213 | JArr := TJSONArray.Create; 214 | for Item in Value do 215 | JArr.Add(Item); 216 | 217 | Add(Key, JArr); 218 | Result := Self; 219 | end; 220 | 221 | procedure TJSONParam.Clear; 222 | begin 223 | FJSON.Free; 224 | FJSON := TJSONObject.Create; 225 | end; 226 | 227 | constructor TJSONParam.Create; 228 | begin 229 | FJSON := TJSONObject.Create; 230 | end; 231 | 232 | procedure TJSONParam.Delete(const Key: string); 233 | var 234 | Item: TJSONPair; 235 | begin 236 | Item := FJSON.RemovePair(Key); 237 | if Assigned(Item) then 238 | Item.Free; 239 | end; 240 | 241 | destructor TJSONParam.Destroy; 242 | begin 243 | if Assigned(FJSON) then 244 | FJSON.Free; 245 | inherited; 246 | end; 247 | 248 | function TJSONParam.GetCount: Integer; 249 | begin 250 | Result := FJSON.Count; 251 | end; 252 | 253 | function TJSONParam.GetOrCreate(const Name: string): T; 254 | begin 255 | if not FJSON.TryGetValue(Name, Result) then 256 | begin 257 | Result := T.Create; 258 | FJSON.AddPair(Name, Result); 259 | end; 260 | end; 261 | 262 | function TJSONParam.GetOrCreateObject(const Name: string): TJSONObject; 263 | begin 264 | Result := GetOrCreate(Name); 265 | end; 266 | 267 | procedure TJSONParam.SetJSON(const Value: TJSONObject); 268 | begin 269 | FJSON := Value; 270 | end; 271 | 272 | function TJSONParam.ToJsonString(FreeObject: Boolean): string; 273 | begin 274 | Result := FJSON.ToJSON; 275 | if FreeObject then 276 | Free; 277 | end; 278 | 279 | function TJSONParam.ToStream: TStringStream; 280 | begin 281 | Result := TStringStream.Create; 282 | try 283 | Result.WriteString(ToJsonString); 284 | Result.Position := 0; 285 | except 286 | Result.Free; 287 | raise; 288 | end; 289 | end; 290 | 291 | function TJSONParam.ToStringPairs: TArray>; 292 | var 293 | Pair: TJSONPair; 294 | begin 295 | for Pair in FJSON do 296 | Result := Result + [TPair.Create(Pair.JsonString.Value, Pair.JsonValue.AsType)]; 297 | end; 298 | 299 | end. 300 | 301 | -------------------------------------------------------------------------------- /OpenAI.Utils.JSON.Cleaner.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Utils.JSON.Cleaner; 2 | 3 | interface 4 | 5 | uses 6 | System.Rtti, 7 | System.JSON, 8 | System.TypInfo, 9 | System.SysUtils, 10 | System.Classes, 11 | System.Generics.Collections; 12 | 13 | type 14 | IJSONCleaner = interface 15 | ['{DB75F095-ADE7-4ABF-8986-F9CCB0FD578D}'] 16 | /// 17 | /// Cleans a JSON string by removing fields that are not present in the corresponding class. 18 | /// 19 | /// The JSON string to be cleaned. 20 | /// A cleaned JSON string. 21 | function CleanJSON(const AJSON: string): string; 22 | end; 23 | 24 | TJSONCleaner = class(TInterfacedObject, IJSONCleaner) 25 | private 26 | /// 27 | /// Cleans a JSON object by removing fields that are not present in the corresponding class. 28 | /// 29 | /// The JSON object to be cleaned. 30 | /// The RTTI type of the corresponding Delphi class. 31 | procedure CleanJSONObject(AJSONObject: TJSONObject; AType: TRttiType); 32 | 33 | /// 34 | /// Cleans a JSON array by applying the cleaning process to each item in the array. 35 | /// 36 | /// The JSON array to be cleaned. 37 | /// The RTTI type of the items in the array. 38 | procedure CleanJSONArray(AJSONArray: TJSONArray; AType: TRttiType); 39 | 40 | /// 41 | /// Obtains the generic type of a list from the RTTI type. 42 | /// 43 | /// The RTTI type of the TObjectList. 44 | /// The name of the list type. 45 | /// The RTTI type of the generic item in the list. 46 | function GetGenericClassType(AType: TRttiType; const ATypeListName: String): TRttiType; 47 | 48 | /// 49 | /// Removes the prefix 'F' (uppercase or lowercase) from the beginning of a string, if present. 50 | /// 51 | /// The string from which the prefix 'F' should be removed. 52 | /// 53 | /// The string without the 'F' prefix at the beginning, if present; otherwise, returns the original string. 54 | /// 55 | function RemovePrefixF(const AValue: string): string; 56 | 57 | public 58 | /// 59 | /// Creates an instance of the class. 60 | /// 61 | /// 62 | /// Returns the instance of the class. 63 | /// 64 | class function New: IJSONCleaner; 65 | 66 | /// 67 | /// Cleans a JSON string by removing fields that are not present in the corresponding class. 68 | /// 69 | /// The JSON string to be cleaned. 70 | /// A cleaned JSON string. 71 | function CleanJSON(const AJSON: string): string; 72 | 73 | end; 74 | 75 | implementation 76 | 77 | uses 78 | REST.JSON.Types; 79 | 80 | procedure TJSONCleaner.CleanJSONObject(AJSONObject: TJSONObject; AType: TRttiType); 81 | var 82 | LField: TRttiField; 83 | LContext: TRttiContext; 84 | LFields: TDictionary; 85 | LPair: TJSONPair; 86 | LListToRemove: TList; 87 | LPairValue: string; 88 | LJSONPair: TJSONPair; 89 | LPropType, LPropTypeAux: TRttiType; 90 | LAttribute: TCustomAttribute; 91 | LFieldName: String; 92 | 93 | begin 94 | if not Assigned(AJSONObject) or (AJSONObject.toJSON.ToLower = 'null') then 95 | Exit; 96 | 97 | LContext := TRttiContext.Create; 98 | try 99 | LFields := TDictionary.Create; 100 | try 101 | for LField in AType.GetFields do 102 | begin 103 | LFieldName := EmptyStr; 104 | 105 | for LAttribute in LField.GetAttributes do 106 | begin 107 | if LAttribute is JSONNameAttribute then 108 | begin 109 | {$IF CompilerVersion >= 36} 110 | LFieldName := JSONNameAttribute(LAttribute).Value; 111 | {$ELSE} 112 | LFieldName := JSONNameAttribute(LAttribute).Name; 113 | {$ENDIF} 114 | break; 115 | end; 116 | end; 117 | 118 | if LFieldName.Trim.IsEmpty then 119 | LFieldName := RemovePrefixF(LField.Name).ToLower; 120 | 121 | LFields.TryAdd(LFieldName.ToLower, LField); 122 | end; 123 | 124 | LListToRemove := TList.Create; 125 | try 126 | for LPair in AJSONObject do 127 | begin 128 | if not LFields.ContainsKey(LPair.JsonString.Value.ToLower) then 129 | LListToRemove.Add(LPair.JsonString.Value) 130 | else 131 | begin 132 | LPropType := LFields[LPair.JsonString.Value.ToLower].FieldType; 133 | if LPropType.TypeKind = tkClass then 134 | begin 135 | if (LPropType.Name.Contains('TList<') or LPropType.Name.Contains('TObjectList<')) and LPropType.IsInstance 136 | then 137 | begin 138 | LPropTypeAux := GetGenericClassType(LPropType, 'System.Generics.Collections.TObjectList<'); 139 | CleanJSONArray(TJSONArray(LPair.JsonValue), LPropTypeAux); 140 | end 141 | else 142 | CleanJSONObject(TJSONObject(LPair.JsonValue), LPropType); 143 | end 144 | else if LPropType.TypeKind = tkDynArray then 145 | begin 146 | LPropTypeAux := GetGenericClassType(LPropType, 'System.TArray<'); 147 | CleanJSONArray(TJSONArray(LPair.JsonValue), LPropTypeAux); 148 | end; 149 | end; 150 | end; 151 | 152 | for LPairValue in LListToRemove do 153 | begin 154 | LJSONPair := AJSONObject.RemovePair(LPairValue); 155 | if Assigned(LJSONPair) then 156 | LJSONPair.Free; 157 | end; 158 | finally 159 | LListToRemove.Free; 160 | end; 161 | finally 162 | LFields.Free; 163 | end; 164 | finally 165 | LContext.Free; 166 | end; 167 | end; 168 | 169 | function TJSONCleaner.GetGenericClassType(AType: TRttiType; const ATypeListName: String): TRttiType; 170 | var 171 | LContext: TRttiContext; 172 | LRttiType: TRttiType; 173 | LBaseType: TRttiType; 174 | LTypeName: string; 175 | LGenTypeName: string; 176 | begin 177 | Result := nil; 178 | LContext := TRttiContext.Create; 179 | try 180 | LBaseType := LContext.GetType(AType.Handle); 181 | if Assigned(LBaseType) then 182 | begin 183 | LTypeName := LBaseType.QualifiedName; 184 | if LTypeName.StartsWith(ATypeListName) then 185 | begin 186 | LGenTypeName := Copy(LTypeName, Pos('<', LTypeName) + 1, Length(LTypeName) - Pos('<', LTypeName) - 1); 187 | LRttiType := LContext.FindType(LGenTypeName); 188 | Result := LRttiType; 189 | end; 190 | 191 | end; 192 | finally 193 | LContext.Free; 194 | end; 195 | end; 196 | 197 | class function TJSONCleaner.New: IJSONCleaner; 198 | begin 199 | Result := Self.Create; 200 | end; 201 | 202 | function TJSONCleaner.RemovePrefixF(const AValue: string): string; 203 | begin 204 | if (Length(AValue) > 0) and SameText(AValue[1], 'F') then 205 | Result := Copy(AValue, 2, Length(AValue) - 1) 206 | else 207 | Result := AValue; 208 | end; 209 | 210 | procedure TJSONCleaner.CleanJSONArray(AJSONArray: TJSONArray; AType: TRttiType); 211 | var 212 | LItem: TJSONValue; 213 | LItemType: TRttiType; 214 | LContext: TRttiContext; 215 | begin 216 | if not Assigned(AJSONArray) or (AJSONArray.toJSON.ToLower = 'null') or (AJSONArray.Count <= 0) then 217 | Exit; 218 | 219 | LContext := TRttiContext.Create; 220 | try 221 | for LItem in AJSONArray do 222 | begin 223 | if LItem is TJSONObject then 224 | begin 225 | LItemType := LContext.GetType(AType.AsInstance.MetaclassType); 226 | CleanJSONObject(TJSONObject(LItem), LItemType); 227 | end 228 | else if LItem is TJSONArray then 229 | begin 230 | CleanJSONArray(TJSONArray(LItem), AType); 231 | end; 232 | end; 233 | finally 234 | LContext.Free; 235 | end; 236 | end; 237 | 238 | function TJSONCleaner.CleanJSON(const AJSON: string): string; 239 | var 240 | LJSONObject: TJSONObject; 241 | LContext: TRttiContext; 242 | LType: TRttiType; 243 | begin 244 | LJSONObject := TJSONObject.ParseJSONValue(AJSON) as TJSONObject; 245 | 246 | if not Assigned(LJSONObject) then 247 | raise Exception.Create('Invalid JSON format'); 248 | 249 | try 250 | LContext := TRttiContext.Create; 251 | try 252 | LType := LContext.GetType(TClass(T)); 253 | CleanJSONObject(LJSONObject, LType); 254 | Result := LJSONObject.toJSON; 255 | finally 256 | LContext.Free; 257 | end; 258 | finally 259 | LJSONObject.Free; 260 | end; 261 | end; 262 | 263 | end. 264 | -------------------------------------------------------------------------------- /OpenAI.Moderations.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Moderations; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API.Params, OpenAI.API, Rest.Json.Types; 7 | 8 | type 9 | TModerationsParams = class(TJSONParam) 10 | /// 11 | /// The input text to classify 12 | /// 13 | function Input(const Value: string): TModerationsParams; overload; 14 | /// 15 | /// The input text to classify 16 | /// 17 | function Input(const Value: TArray): TModerationsParams; overload; 18 | /// 19 | /// Two content moderations models are available: "text-moderation-stable" and "text-moderation-latest". 20 | /// The default is text-moderation-latest which will be automatically upgraded over time. 21 | /// This ensures you are always using our most accurate model. If you use text-moderation-stable, 22 | /// we will provide advanced notice before updating the model. Accuracy of text-moderation-stable may be 23 | /// slightly lower than for text-moderation-latest. 24 | /// 25 | function Model(const Value: string = 'text-moderation-latest'): TModerationsParams; 26 | constructor Create; reintroduce; 27 | end; 28 | 29 | TCategoryScores = class 30 | private 31 | FHate: Extended; 32 | [JSONName('hate/threatening')] 33 | FHatethreatening: Extended; 34 | [JSONName('self-harm')] 35 | FSelfharm: Extended; 36 | FSexual: Extended; 37 | [JSONName('sexual/minors')] 38 | FSexualminors: Extended; 39 | FViolence: Extended; 40 | [JSONName('violence/graphic')] 41 | FViolencegraphic: Extended; 42 | FHarassment: Extended; 43 | [JSONName('harassment/threatening')] 44 | FHarassmentthreatening: Extended; 45 | [JSONName('self-harm/intent')] 46 | FSelfharmintent: Extended; 47 | [JSONName('self-harm/instructions')] 48 | FSelfharminstructions: Extended; 49 | public 50 | /// 51 | /// Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, 52 | /// sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) 53 | /// is harrassment. 54 | /// 55 | property Hate: Extended read FHate write FHate; 56 | /// 57 | /// Hateful content that also includes violence or serious harm towards the targeted group based on race, 58 | /// gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. 59 | /// 60 | property HateOrThreatening: Extended read FHatethreatening write FHatethreatening; 61 | /// 62 | /// Content that expresses, incites, or promotes harassing language towards any target. 63 | /// 64 | property Harassment: Extended read FHarassment write FHarassment; 65 | /// 66 | /// Harassment content that also includes violence or serious harm towards any target. 67 | /// 68 | property HarassmentOrThreatening: Extended read FHarassmentthreatening write FHarassmentthreatening; 69 | /// 70 | /// Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders. 71 | /// 72 | property SelfHarm: Extended read FSelfharm write FSelfharm; 73 | /// 74 | /// Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, 75 | /// such as suicide, cutting, and eating disorders. 76 | /// 77 | property SelfHarmOrIntent: Extended read FSelfharmintent write FSelfharmintent; 78 | /// 79 | /// Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, 80 | /// or that gives instructions or advice on how to commit such acts. 81 | /// 82 | property SelfHarmOrInstructions: Extended read FSelfharminstructions write FSelfharminstructions; 83 | /// 84 | /// Content meant to arouse sexual excitement, such as the description of sexual activity, 85 | /// or that promotes sexual services (excluding sex education and wellness). 86 | /// 87 | property Sexual: Extended read FSexual write FSexual; 88 | /// 89 | /// Sexual content that includes an individual who is under 18 years old. 90 | /// 91 | property SexualOrMinors: Extended read FSexualminors write FSexualminors; 92 | /// 93 | /// Content that depicts death, violence, or physical injury. 94 | /// 95 | property Violence: Extended read FViolence write FViolence; 96 | /// 97 | /// Content that depicts death, violence, or physical injury in graphic detail. 98 | /// 99 | property ViolenceOrGraphic: Extended read FViolencegraphic write FViolencegraphic; 100 | end; 101 | 102 | TCategories = class 103 | private 104 | FHate: Boolean; 105 | [JSONName('hate/threatening')] 106 | FHatethreatening: Boolean; 107 | [JSONName('self-harm')] 108 | FSelfharm: Boolean; 109 | FSexual: Boolean; 110 | [JSONName('sexual/minors')] 111 | FSexualminors: Boolean; 112 | FViolence: Boolean; 113 | [JSONName('violence/graphic')] 114 | FViolencegraphic: Boolean; 115 | FHarassment: Boolean; 116 | [JSONName('harassment/threatening')] 117 | FHarassmentthreatening: Boolean; 118 | [JSONName('self-harm/intent')] 119 | FSelfharmintent: Boolean; 120 | [JSONName('self-harm/instructions')] 121 | FSelfharminstructions: Boolean; 122 | public 123 | /// 124 | /// Content that expresses, incites, or promotes hate based on race, gender, ethnicity, religion, nationality, 125 | /// sexual orientation, disability status, or caste. Hateful content aimed at non-protected groups (e.g., chess players) 126 | /// is harrassment. 127 | /// 128 | property Hate: Boolean read FHate write FHate; 129 | /// 130 | /// Hateful content that also includes violence or serious harm towards the targeted group based on race, 131 | /// gender, ethnicity, religion, nationality, sexual orientation, disability status, or caste. 132 | /// 133 | property HateOrThreatening: Boolean read FHatethreatening write FHatethreatening; 134 | /// 135 | /// Content that expresses, incites, or promotes harassing language towards any target. 136 | /// 137 | property Harassment: Boolean read FHarassment write FHarassment; 138 | /// 139 | /// Harassment content that also includes violence or serious harm towards any target. 140 | /// 141 | property HarassmentOrThreatening: Boolean read FHarassmentthreatening write FHarassmentthreatening; 142 | /// 143 | /// Content that promotes, encourages, or depicts acts of self-harm, such as suicide, cutting, and eating disorders. 144 | /// 145 | property SelfHarm: Boolean read FSelfharm write FSelfharm; 146 | /// 147 | /// Content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, 148 | /// such as suicide, cutting, and eating disorders. 149 | /// 150 | property SelfHarmOrIntent: Boolean read FSelfharmintent write FSelfharmintent; 151 | /// 152 | /// Content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, 153 | /// or that gives instructions or advice on how to commit such acts. 154 | /// 155 | property SelfHarmOrInstructions: Boolean read FSelfharminstructions write FSelfharminstructions; 156 | /// 157 | /// Content meant to arouse sexual excitement, such as the description of sexual activity, 158 | /// or that promotes sexual services (excluding sex education and wellness). 159 | /// 160 | property Sexual: Boolean read FSexual write FSexual; 161 | /// 162 | /// Sexual content that includes an individual who is under 18 years old. 163 | /// 164 | property SexualOrMinors: Boolean read FSexualminors write FSexualminors; 165 | /// 166 | /// Content that depicts death, violence, or physical injury. 167 | /// 168 | property Violence: Boolean read FViolence write FViolence; 169 | /// 170 | /// Content that depicts death, violence, or physical injury in graphic detail. 171 | /// 172 | property ViolenceOrGraphic: Boolean read FViolencegraphic write FViolencegraphic; 173 | end; 174 | 175 | TResult = class 176 | private 177 | FCategories: TCategories; 178 | FCategory_scores: TCategoryScores; 179 | FFlagged: Boolean; 180 | public 181 | /// 182 | /// A list of the categories, and whether they are flagged or not. 183 | /// 184 | property Categories: TCategories read FCategories write FCategories; 185 | /// 186 | /// A list of the categories along with their scores as predicted by model. 187 | /// 188 | property CategoryScores: TCategoryScores read FCategory_scores write FCategory_scores; 189 | /// 190 | /// Whether the content violates OpenAI's usage policies. 191 | /// 192 | /// https://platform.openai.com/policies/usage-policies 193 | property Flagged: Boolean read FFlagged write FFlagged; 194 | destructor Destroy; override; 195 | end; 196 | 197 | /// 198 | /// Represents policy compliance report by OpenAI's content moderation model against a given input. 199 | /// 200 | TModerations = class 201 | private 202 | FId: string; 203 | FModel: string; 204 | FResults: TArray; 205 | public 206 | /// 207 | /// The unique identifier for the moderation request. 208 | /// 209 | property Id: string read FId write FId; 210 | /// 211 | /// The model used to generate the moderation results. 212 | /// 213 | property Model: string read FModel write FModel; 214 | /// 215 | /// A list of moderation objects. 216 | /// 217 | property Results: TArray read FResults write FResults; 218 | destructor Destroy; override; 219 | end; 220 | 221 | TModerationsRoute = class(TOpenAIAPIRoute) 222 | public 223 | /// 224 | /// Classifies if text violates OpenAI's Content Policy 225 | /// 226 | function Create(ParamProc: TProc): TModerations; 227 | end; 228 | 229 | implementation 230 | 231 | { TModerationsRoute } 232 | 233 | function TModerationsRoute.Create(ParamProc: TProc): TModerations; 234 | begin 235 | Result := API.Post('moderations', ParamProc); 236 | end; 237 | 238 | { TModerationsParams } 239 | 240 | constructor TModerationsParams.Create; 241 | begin 242 | inherited; 243 | Model(); 244 | end; 245 | 246 | function TModerationsParams.Input(const Value: TArray): TModerationsParams; 247 | begin 248 | Result := TModerationsParams(Add('input', Value)); 249 | end; 250 | 251 | function TModerationsParams.Model(const Value: string): TModerationsParams; 252 | begin 253 | Result := TModerationsParams(Add('model', Value)); 254 | end; 255 | 256 | function TModerationsParams.Input(const Value: string): TModerationsParams; 257 | begin 258 | Result := TModerationsParams(Add('input', Value)); 259 | end; 260 | 261 | { TResult } 262 | 263 | destructor TResult.Destroy; 264 | begin 265 | FCategories.Free; 266 | FCategory_scores.Free; 267 | inherited; 268 | end; 269 | 270 | { TModerations } 271 | 272 | destructor TModerations.Destroy; 273 | var 274 | Item: TResult; 275 | begin 276 | for Item in FResults do 277 | Item.Free; 278 | inherited; 279 | end; 280 | 281 | end. 282 | 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delphi OpenAI API 2 | 3 | ![logo](https://github.com/HemulGM/DelphiOpenAI/blob/main/openai+delphi.png?raw=true) 4 | 5 | ___ 6 | ![GitHub](https://img.shields.io/github/license/hemulgm/DelphiOpenAI) 7 | ![GitHub](https://img.shields.io/github/last-commit/hemulgm/DelphiOpenAI) 8 | ![GitHub](https://img.shields.io/badge/coverage-100%25-green) 9 | ![GitHub](https://img.shields.io/badge/IDE%20Version-Delphi%2011+-yellow) 10 | ![GitHub](https://img.shields.io/badge/platform-all%20platforms-green) 11 | 12 | This repositorty contains Delphi implementation over [OpenAI](https://beta.openai.com/docs/api-reference/) public API. 13 | 14 | ❗*This is an unofficial library. OpenAI does not provide any official library for Delphi.* 15 | 16 | ### Compatibility 17 | Also, the library is compatible with the following AI APIs (tested): 18 | - OpenAI Azure 19 | - DeepSeek 20 | - YandexGPT 21 | - Qwen 22 | - GigaChat 23 | 24 | and other compatible with the OpenAI API. 25 | 26 | ### Table of contents 27 | 28 | - [What is OpenAI](#what-is-openai) 29 | - [Installation](#installation) 30 | - [Usage](#usage) 31 | - [Initialization](#initialization) 32 | - [Models](#models) 33 | - [Completions](#completions) 34 | - [Chats](#chats) 35 | - [Images](#images) 36 | - [Function calling](#function-calling) 37 | - [Errors](#errors) 38 | - [Exceptions](#exceptions) 39 | - [Usage proxy](#proxy) 40 | - [Preparing mechanism](#preparing-mechanism) 41 | - [Examples](#examples) 42 | - [Requirements](#requirements) 43 | - [Links](#links) 44 | - [License](#license) 45 | 46 |
47 | Coverage 48 | 49 | |API|Status| 50 | |---|---| 51 | |Models|🟢 Done| 52 | |Completions (Legacy)|🟢 Done| 53 | |Chat|🟢 Done| 54 | |Chat Vision|🟢 Done| 55 | |Edits|🟢 Done| 56 | |Images|🟢 Done| 57 | |Embeddings|🟢 Done| 58 | |Audio|🟢 Done| 59 | |Files|🟢 Done| 60 | |Fine-tunes (Depricated)|🟢 Done| 61 | |Fine-tuning|🟢 Done| 62 | |Moderations|🟢 Done| 63 | |Engines (Depricated)|🟢 Done| 64 | |Assistants|🟠 In progress| 65 | |Threads|🟠 In progress| 66 | |Messages|🟠 In progress| 67 | |Runs|🟠 In progress| 68 | 69 |
70 | 71 | ## What is OpenAI 72 | 73 | OpenAI is a non-profit artificial intelligence research organization founded in San Francisco, California in 2015. It was created with the purpose of advancing digital intelligence in ways that benefit humanity as a whole and promote societal progress. The organization strives to develop AI (Artificial Intelligence) programs and systems that can think, act and adapt quickly on their own – autonomously. OpenAI's mission is to ensure safe and responsible use of AI for civic good, economic growth and other public benefits; this includes cutting-edge research into important topics such as general AI safety, natural language processing, applied reinforcement learning methods, machine vision algorithms etc. 74 | 75 | >The OpenAI API can be applied to virtually any task that involves understanding or generating natural language or code. We offer a spectrum of models with different levels of power suitable for different tasks, as well as the ability to fine-tune your own custom models. These models can be used for everything from content generation to semantic search and classification. 76 | 77 | This library provides access to the API of the [OpenAI service](https://openai.com/api/), on the basis of which [ChatGPT](https://openai.com/blog/chatgpt) works and, for example, the generation of images from text using `DALL-E`. 78 | 79 | ## Installation 80 | 81 | You can install the package from `GetIt` [directly](https://getitnow.embarcadero.com/openai-for-delphi) in the IDE. Or, to use the library, just add the `root` folder to the IDE library path, or your project source path. 82 | 83 | ## Usage 84 | 85 | ### Initialization 86 | 87 | To initialize API instance you need to [obtain](https://beta.openai.com/account/api-keys) API token from your Open AI organization. 88 | 89 | Once you have a token, you can initialize `TOpenAI` class, which is an entry point to the API. 90 | 91 | Due to the fact that there can be many parameters and not all of them are required, they are configured using an anonymous function. 92 | 93 | ```Pascal 94 | uses OpenAI; 95 | 96 | var OpenAI := TOpenAIComponent.Create(Self, API_TOKEN); 97 | ``` 98 | 99 | or 100 | 101 | ```Pascal 102 | uses OpenAI; 103 | 104 | var OpenAI: IOpenAI := TOpenAI.Create(API_TOKEN); 105 | ``` 106 | 107 | Once token you posses the token, and the instance is initialized you are ready to make requests. 108 | 109 | ### Models 110 | List and describe the various models available in the API. You can refer to the Models documentation to understand what models are available and the differences between them. 111 | 112 | ```Pascal 113 | var Models := OpenAI.Model.List(); 114 | try 115 | for var Model in Models.Data do 116 | MemoChat.Lines.Add(Model.Id); 117 | finally 118 | Models.Free; 119 | end; 120 | ``` 121 | 122 | Review [Models Documentation](https://platform.openai.com/docs/api-reference/models) for more info. 123 | 124 | ### Completions 125 | Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. 126 | 127 | ```Pascal 128 | var Completions := OpenAI.Completion.Create( 129 | procedure(Params: TCompletionParams) 130 | begin 131 | Params.Prompt(MemoPrompt.Text); 132 | Params.MaxTokens(2048); 133 | end); 134 | try 135 | for var Choice in Completions.Choices do 136 | MemoChat.Lines.Add(Choice.Index.ToString + ' ' + Choice.Text); 137 | finally 138 | Completions.Free; 139 | end; 140 | ``` 141 | 142 | Review [Completions Documentation](https://platform.openai.com/docs/api-reference/completions) for more info. 143 | 144 | ### Chats 145 | Given a chat conversation, the model will return a chat completion response. 146 | ChatGPT is powered by gpt-3.5-turbo, OpenAI’s most advanced language model. 147 | 148 | Using the OpenAI API, you can build your own applications with gpt-3.5-turbo to do things like: 149 | - Draft an email or other piece of writing 150 | - Write Python code 151 | - Answer questions about a set of documents 152 | - Create conversational agents 153 | - Give your software a natural language interface 154 | - Tutor in a range of subjects 155 | - Translate languages 156 | - Simulate characters for video games and much more 157 | 158 | This guide explains how to make an API call for chat-based language models and shares tips for getting good results. 159 | 160 | ```Pascal 161 | var Chat := OpenAI.Chat.Create( 162 | procedure(Params: TChatParams) 163 | begin 164 | Params.Messages([TChatMessageBuild.Create(TMessageRole.User, Text)]); 165 | Params.MaxTokens(1024); 166 | end); 167 | try 168 | for var Choice in Chat.Choices do 169 | MemoChat.Lines.Add(Choice.Message.Content); 170 | finally 171 | Chat.Free; 172 | end; 173 | ``` 174 | 175 | #### Stream mode 176 | ```Pascal 177 | OpenAI.Chat.CreateStream( 178 | procedure(Params: TChatParams) 179 | begin 180 | Params.Messages([TchatMessageBuild.User(Buf.Text)]); 181 | Params.MaxTokens(1024); 182 | Params.Stream; 183 | end, 184 | procedure(Chat: TChat; IsDone: Boolean; var Cancel: Boolean) 185 | begin 186 | if (not IsDone) and Assigned(Chat) then 187 | Writeln(Chat.Choices[0].Delta.Content) 188 | else if IsDone then 189 | Writeln('DONE!'); 190 | Writeln('-------'); 191 | Sleep(100); 192 | end); 193 | ``` 194 | 195 | #### Vision 196 | ```Pascal 197 | var Chat := OpenAI.Chat.Create( 198 | procedure(Params: TChatParams) 199 | begin 200 | Params.Model('gpt-4-vision-preview'); 201 | 202 | var Content: TArray; 203 | Content := Content + [TMessageContent.CreateText(Text)]; 204 | Content := Content + [TMessageContent.CreateImage(FileToBase64('file path'))]; 205 | 206 | Params.Messages([TChatMessageBuild.User(Content)]); 207 | Params.MaxTokens(1024); 208 | end); 209 | try 210 | for var Choice in Chat.Choices do 211 | MemoChat.Lines.Add(Choice.Message.Content); 212 | finally 213 | Chat.Free; 214 | end; 215 | ``` 216 | 217 | Review [Chat Documentation](https://platform.openai.com/docs/api-reference/chat) for more info. 218 | 219 | ### Images 220 | Given a prompt and/or an input image, the model will generate a new image. 221 | 222 | ```Pascal 223 | var Images := OpenAI.Image.Create( 224 | procedure(Params: TImageCreateParams) 225 | begin 226 | Params.Prompt(MemoPrompt.Text); 227 | Params.ResponseFormat('url'); 228 | end); 229 | try 230 | for var Image in Images.Data do 231 | Image1.Bitmap.LoadFromUrl(Image.Url); 232 | finally 233 | Images.Free; 234 | end; 235 | ``` 236 | 237 | Review [Images Documentation](https://platform.openai.com/docs/api-reference/images) for more info. 238 | 239 | ### Function Calling 240 | In an API call, you can describe functions to gpt-3.5-turbo-0613 and gpt-4-0613, and have the model intelligently choose to output a JSON object containing arguments to call those functions. The Chat Completions API does not call the function; instead, the model generates JSON that you can use to call the function in your code. 241 | 242 | The latest models (gpt-3.5-turbo-0613 and gpt-4-0613) have been fine-tuned to both detect when a function should to be called (depending on the input) and to respond with JSON that adheres to the function signature. With this capability also comes potential risks. We strongly recommend building in user confirmation flows before taking actions that impact the world on behalf of users (sending an email, posting something online, making a purchase, etc). 243 | 244 | ```Pascal 245 | var Chat := OpenAI.Chat.Create( 246 | procedure(Params: TChatParams) 247 | begin 248 | Params.Functions(Funcs); //list of functions (TArray) 249 | Params.FunctionCall(TFunctionCall.Auto); 250 | Params.Messages([TChatMessageBuild.User(Text)]); 251 | Params.MaxTokens(1024); 252 | end); 253 | try 254 | for var Choice in Chat.Choices do 255 | if Choice.FinishReason = TFinishReason.FunctionCall then 256 | ProcFunction(Choice.Message.FunctionCall) // execute function (send result to chat, and continue) 257 | else 258 | MemoChat.Lines.Add(Choice.Message.Content); 259 | finally 260 | Chat.Free; 261 | end; 262 | 263 | ... 264 | 265 | procedure ProcFunction(Func: TChatFunctionCall); 266 | begin 267 | var FuncResult := Execute(Func.Name, Func.Arguments); //execute function and get result (json) 268 | var Chat := OpenAI.Chat.Create( 269 | procedure(Params: TChatParams) 270 | begin 271 | Params.Functions(Funcs); //list of functions (TArray) 272 | Params.FunctionCall(TFunctionCall.Auto); 273 | Params.Messages([ //need all history 274 | TChatMessageBuild.User(Text), 275 | TChatMessageBuild.NewAsistantFunc(Func.Name, Func.Arguments), 276 | TChatMessageBuild.Func(FuncResult, Func.Name)]); 277 | Params.MaxTokens(1024); 278 | end); 279 | try 280 | for var Choice in Chat.Choices do 281 | MemoChat.Lines.Add(Choice.Message.Content); 282 | finally 283 | Chat.Free; 284 | end; 285 | end; 286 | ``` 287 | 288 | Review [Functions Documentation](https://platform.openai.com/docs/guides/gpt/function-calling) for more info. 289 | 290 | ### Errors 291 | 292 | ```Pascal 293 | try 294 | var Images := OpenAI.Image.Create(...); 295 | except 296 | on E: OpenAIExceptionRateLimitError do 297 | ShowError('OpenAI Limit Error: ' + E.Message); 298 | on E: OpenAIException do 299 | ShowError('OpenAI Error: ' + E.Message); 300 | end; 301 | ``` 302 | 303 | #### Exceptions 304 | 305 | * OpenAIExceptionAPI - errors of wrapper 306 | * OpenAIException - base exception 307 | * OpenAIExceptionInvalidRequestError 308 | * OpenAIExceptionRateLimitError 309 | * OpenAIExceptionAuthenticationError 310 | * OpenAIExceptionPermissionError 311 | * OpenAIExceptionTryAgain 312 | * OpenAIExceptionInvalidResponse - parse error 313 | 314 | ### Proxy 315 | 316 | ```Pascal 317 | OpenAI.API.ProxySettings := TProxySettings.Create( 318 | ProxyHost, 319 | ProxyPort, 320 | ProxyUserName, 321 | ProxyPassword); 322 | ``` 323 | 324 | ### Preparing mechanism 325 | 326 | If a third-party API requires some actions before making a request, then use the IAPIPrepare interface and the Prepare property. 327 | 328 | ```Pascal 329 | var client := TOpenAI.Create('https://gigachat.devices.sberbank.ru/api/v1', ''); 330 | сlient.Prepare := TApiPrepareGigaChat.Create( 331 | '', 332 | ''); 333 | var response := client.Chat.Create(...); 334 | ``` 335 | 336 | ## Examples 337 | 338 | 339 | [ChatGPT (FMX)](https://github.com/HemulGM/ChatGPT) 340 | 341 | ## Requirements 342 | This library does not require any 3rd party library. It works on recent Delphi versions (10.3+). Althought not fully tested, it should also work on all supported platforms (Windows, Linux, macOS, Android, iOS). 343 | 344 | Since the library requires your secret API key, it's not recommended you use it on client applications, as your secret key will be exposed, unless you are sure about the security risks. 345 | 346 | ## Links 347 | 348 | - [OpenAI Documentation](https://platform.openai.com/docs/introduction) 349 | - [OpenAI Playground](https://beta.openai.com/playground) 350 | - [OpenAI Examples](https://beta.openai.com/examples) 351 | - [Dall-E](https://labs.openai.com/) 352 | 353 | ## License 354 | 355 | ``` 356 | MIT License 357 | 358 | Copyright (c) 2023 HemulGM 359 | 360 | Permission is hereby granted, free of charge, to any person obtaining a copy 361 | of this software and associated documentation files (the "Software"), to deal 362 | in the Software without restriction, including without limitation the rights 363 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 364 | copies of the Software, and to permit persons to whom the Software is 365 | furnished to do so, subject to the following conditions: 366 | 367 | The above copyright notice and this permission notice shall be included in all 368 | copies or substantial portions of the Software. 369 | 370 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 371 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 372 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 373 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 374 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 375 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 376 | SOFTWARE. 377 | ``` 378 | 379 | 380 |
381 |

382 | Delphi 383 |

384 |
385 | Made with :heart: on Delphi 386 |
387 | -------------------------------------------------------------------------------- /OpenAI.Completions.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Completions; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, OpenAI.API.Params, OpenAI.API, System.JSON; 7 | 8 | type 9 | TCompletionParams = class(TJSONParam) 10 | /// 11 | /// ID of the model to use. You can use the List models API to see all of your available models, 12 | /// or see our Model overview for descriptions of them. 13 | /// 14 | function Model(const Value: string): TCompletionParams; 15 | /// 16 | /// The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. 17 | /// Note that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. 18 | /// 19 | function Prompt(const Value: string): TCompletionParams; overload; 20 | /// 21 | /// The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. 22 | /// Note that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. 23 | /// 24 | function Prompt(const Value: TArray): TCompletionParams; overload; 25 | /// 26 | /// The suffix that comes after a completion of inserted text. 27 | /// 28 | function Suffix(const Value: string = ''): TCompletionParams; overload; 29 | /// 30 | /// The maximum number of tokens to generate in the completion. 31 | /// The token count of your prompt plus max_tokens cannot exceed the model's context length. 32 | /// Most models have a context length of 2048 tokens (except for the newest models, which support 4096). 33 | /// 34 | function MaxTokens(const Value: Integer = 16): TCompletionParams; 35 | /// 36 | /// What sampling temperature to use. Higher values means the model will take more risks. 37 | /// Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. 38 | /// We generally recommend altering this or top_p but not both. 39 | /// 40 | function Temperature(const Value: Single = 1): TCompletionParams; 41 | /// 42 | /// An alternative to sampling with temperature, called nucleus sampling, 43 | /// where the model considers the results of the tokens with top_p probability mass. 44 | /// So 0.1 means only the tokens comprising the top 10% probability mass are considered. 45 | /// We generally recommend altering this or temperature but not both. 46 | /// 47 | function TopP(const Value: Single = 1): TCompletionParams; 48 | /// 49 | /// How many completions to generate for each prompt. 50 | /// Note: Because this parameter generates many completions, it can quickly consume your token quota. 51 | /// Use carefully and ensure that you have reasonable settings for max_tokens and stop. 52 | /// 53 | function N(const Value: Integer = 1): TCompletionParams; 54 | /// 55 | /// Whether to stream back partial progress. 56 | /// If set, tokens will be sent as data-only server-sent events as they become available, 57 | /// with the stream terminated by a data: [DONE] message. 58 | /// 59 | function Stream(const Value: Boolean = True): TCompletionParams; 60 | /// 61 | /// Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. 62 | /// For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. 63 | /// The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response. 64 | /// The maximum value for logprobs is 5. If you need more than this, please contact us through our Help center and 65 | /// describe your use case. 66 | /// 67 | function LogProbs(const Value: Integer): TCompletionParams; 68 | /// 69 | /// Echo back the prompt in addition to the completion 70 | /// 71 | function Echo(const Value: Boolean = True): TCompletionParams; 72 | /// 73 | /// Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. 74 | /// 75 | function Stop(const Value: string): TCompletionParams; overload; 76 | /// 77 | /// Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. 78 | /// 79 | function Stop(const Value: TArray): TCompletionParams; overload; 80 | /// 81 | /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, 82 | /// increasing the model's likelihood to talk about new topics. 83 | /// 84 | function PresencePenalty(const Value: Single = 0): TCompletionParams; 85 | /// 86 | /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, 87 | /// decreasing the model's likelihood to repeat the same line verbatim. 88 | /// 89 | function FrequencyPenalty(const Value: Single = 0): TCompletionParams; 90 | /// 91 | /// Generates best_of completions server-side and returns the "best" (the one with the highest log probability per token). 92 | /// Results cannot be streamed. 93 | /// When used with n, best_of controls the number of candidate completions and n specifies how many to return – best_of must be greater than n. 94 | /// Note: Because this parameter generates many completions, it can quickly consume your token quota. 95 | /// Use carefully and ensure that you have reasonable settings for max_tokens and stop 96 | /// 97 | function BestOf(const Value: Integer = 1): TCompletionParams; 98 | /// 99 | /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse 100 | /// 101 | function User(const Value: string): TCompletionParams; 102 | /// 103 | /// Modify the likelihood of specified tokens appearing in the completion. 104 | /// 105 | /// Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias 106 | /// value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to 107 | /// token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. 108 | /// The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; 109 | /// values like -100 or 100 should result in a ban or exclusive selection of the relevant token. 110 | /// 111 | /// As an example, you can pass {"50256": -100} to prevent the <|endoftext|> token from being generated. 112 | /// 113 | function LogitBias(const Value: TJSONObject): TCompletionParams; 114 | constructor Create; override; 115 | end; 116 | 117 | TCompletionUsage = class 118 | private 119 | FCompletion_tokens: Int64; 120 | FPrompt_tokens: Int64; 121 | FTotal_tokens: Int64; 122 | public 123 | property CompletionTokens: Int64 read FCompletion_tokens write FCompletion_tokens; 124 | property PromptTokens: Int64 read FPrompt_tokens write FPrompt_tokens; 125 | property TotalTokens: Int64 read FTotal_tokens write FTotal_tokens; 126 | end; 127 | 128 | TCompletionChoices = class 129 | private 130 | FFinish_reason: string; 131 | FIndex: Int64; 132 | FText: string; 133 | public 134 | property FinishReason: string read FFinish_reason write FFinish_reason; 135 | //property Logprobs: string read FFinish_reason write FFinish_reason; 136 | property Index: Int64 read FIndex write FIndex; 137 | property Text: string read FText write FText; 138 | end; 139 | 140 | TCompletions = class 141 | private 142 | FChoices: TArray; 143 | FCreated: Int64; 144 | FId: string; 145 | FModel: string; 146 | FObject: string; 147 | FUsage: TCompletionUsage; 148 | public 149 | property Choices: TArray read FChoices write FChoices; 150 | property Created: Int64 read FCreated write FCreated; 151 | property Id: string read FId write FId; 152 | property Model: string read FModel write FModel; 153 | property &Object: string read FObject write FObject; 154 | property Usage: TCompletionUsage read FUsage write FUsage; 155 | destructor Destroy; override; 156 | end; 157 | 158 | TCompletionEvent = reference to procedure(Completions: TCompletions; IsDone: Boolean; var Cancel: Boolean); 159 | 160 | TCompletionsRoute = class(TOpenAIAPIRoute) 161 | public 162 | /// 163 | /// Creates a completion for the provided prompt and parameters 164 | /// 165 | function Create(ParamProc: TProc): TCompletions; 166 | /// 167 | /// Creates a completion for the provided prompt and parameters (for Stream is true parameter) 168 | /// 169 | function CreateStream(ParamProc: TProc; Event: TCompletionEvent): Boolean; 170 | end; 171 | 172 | implementation 173 | 174 | uses 175 | REST.Json, System.Net.HttpClient; 176 | 177 | { TCompletionsRoute } 178 | 179 | function TCompletionsRoute.Create(ParamProc: TProc): TCompletions; 180 | begin 181 | Result := API.Post('completions', ParamProc); 182 | end; 183 | 184 | function TCompletionsRoute.CreateStream(ParamProc: TProc; Event: TCompletionEvent): Boolean; 185 | var 186 | Response: TStringStream; 187 | RetPos: Integer; 188 | begin 189 | Response := TStringStream.Create('', TEncoding.UTF8); 190 | try 191 | RetPos := 0; 192 | Result := API.Post('completions', ParamProc, Response, 193 | TReceiveDataCallback(procedure(const Sender: TObject; AContentLength: Int64; AReadCount: Int64; var AAbort: Boolean) 194 | var 195 | IsDone: Boolean; 196 | Data: string; 197 | Completions: TCompletions; 198 | TextBuffer: string; 199 | Line: string; 200 | Ret: Integer; 201 | begin 202 | TextBuffer := Response.DataString; 203 | repeat 204 | Ret := TextBuffer.IndexOf(#10, RetPos); 205 | if Ret >= 0 then 206 | begin 207 | Line := TextBuffer.Substring(RetPos, Ret - RetPos); 208 | RetPos := Ret + 1; 209 | if Line.IsEmpty or (Line.StartsWith(#10)) then 210 | Continue; 211 | Completions := nil; 212 | Data := Line.Replace('data: ', '').Trim([' ', #13, #10]); 213 | IsDone := Data = '[DONE]'; 214 | if not IsDone then 215 | begin 216 | try 217 | Completions := TJson.JsonToObject(Data); 218 | except 219 | Completions := nil; 220 | end; 221 | end; 222 | try 223 | Event(Completions, IsDone, AAbort); 224 | finally 225 | if Assigned(Completions) then 226 | Completions.Free; 227 | end; 228 | end; 229 | until Ret < 0; 230 | end)); 231 | finally 232 | Response.Free; 233 | end; 234 | end; 235 | 236 | { TCompletions } 237 | 238 | destructor TCompletions.Destroy; 239 | var 240 | Item: TCompletionChoices; 241 | begin 242 | if Assigned(FUsage) then 243 | FUsage.Free; 244 | for Item in FChoices do 245 | if Assigned(Item) then 246 | Item.Free; 247 | inherited; 248 | end; 249 | 250 | { TCompletionParams } 251 | 252 | function TCompletionParams.BestOf(const Value: Integer): TCompletionParams; 253 | begin 254 | Result := TCompletionParams(Add('best_of', Value)); 255 | end; 256 | 257 | constructor TCompletionParams.Create; 258 | begin 259 | inherited; 260 | Model('text-davinci-003'); 261 | Temperature(0); 262 | end; 263 | 264 | function TCompletionParams.Echo(const Value: Boolean): TCompletionParams; 265 | begin 266 | Result := TCompletionParams(Add('echo', Value)); 267 | end; 268 | 269 | function TCompletionParams.FrequencyPenalty(const Value: Single): TCompletionParams; 270 | begin 271 | Result := TCompletionParams(Add('frequency_penalty', Value)); 272 | end; 273 | 274 | function TCompletionParams.LogitBias(const Value: TJSONObject): TCompletionParams; 275 | begin 276 | Result := TCompletionParams(Add('logit_bias', Value)); 277 | end; 278 | 279 | function TCompletionParams.LogProbs(const Value: Integer): TCompletionParams; 280 | begin 281 | Result := TCompletionParams(Add('logprobs', Value)); 282 | end; 283 | 284 | function TCompletionParams.MaxTokens(const Value: Integer): TCompletionParams; 285 | begin 286 | Result := TCompletionParams(Add('max_tokens', Value)); 287 | end; 288 | 289 | function TCompletionParams.Model(const Value: string): TCompletionParams; 290 | begin 291 | Result := TCompletionParams(Add('model', Value)); 292 | end; 293 | 294 | function TCompletionParams.N(const Value: Integer): TCompletionParams; 295 | begin 296 | Result := TCompletionParams(Add('n', Value)); 297 | end; 298 | 299 | function TCompletionParams.PresencePenalty(const Value: Single): TCompletionParams; 300 | begin 301 | Result := TCompletionParams(Add('presence_penalty', Value)); 302 | end; 303 | 304 | function TCompletionParams.Prompt(const Value: string): TCompletionParams; 305 | begin 306 | Result := TCompletionParams(Add('prompt', Value)); 307 | end; 308 | 309 | function TCompletionParams.Prompt(const Value: TArray): TCompletionParams; 310 | begin 311 | Result := TCompletionParams(Add('prompt', Value)); 312 | end; 313 | 314 | function TCompletionParams.Stop(const Value: TArray): TCompletionParams; 315 | begin 316 | Result := TCompletionParams(Add('stop', Value)); 317 | end; 318 | 319 | function TCompletionParams.Stop(const Value: string): TCompletionParams; 320 | begin 321 | Result := TCompletionParams(Add('stop', Value)); 322 | end; 323 | 324 | function TCompletionParams.Stream(const Value: Boolean): TCompletionParams; 325 | begin 326 | Result := TCompletionParams(Add('stream', Value)); 327 | end; 328 | 329 | function TCompletionParams.Suffix(const Value: string): TCompletionParams; 330 | begin 331 | Result := TCompletionParams(Add('suffix', Value)); 332 | end; 333 | 334 | function TCompletionParams.Temperature(const Value: Single): TCompletionParams; 335 | begin 336 | Result := TCompletionParams(Add('temperature', Value)); 337 | end; 338 | 339 | function TCompletionParams.TopP(const Value: Single): TCompletionParams; 340 | begin 341 | Result := TCompletionParams(Add('top_p', Value)); 342 | end; 343 | 344 | function TCompletionParams.User(const Value: string): TCompletionParams; 345 | begin 346 | Result := TCompletionParams(Add('user', Value)); 347 | end; 348 | 349 | end. 350 | 351 | -------------------------------------------------------------------------------- /OpenAI.FineTunes.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.FineTunes; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, OpenAI.API, OpenAI.API.Params, OpenAI.Files, OpenAI.Types; 7 | 8 | type 9 | TFineTuneCreateParams = class(TJSONParam) 10 | /// 11 | /// The ID of an uploaded file that contains training data. 12 | /// See upload file for how to upload a file. 13 | /// Your dataset must be formatted as a JSONL file, where each training example is a 14 | /// JSON object with the keys "prompt" and "completion". 15 | /// Additionally, you must upload your file with the purpose fine-tune. 16 | /// See the fine-tuning guide for more details. 17 | /// 18 | function TrainingFile(const Value: string): TFineTuneCreateParams; 19 | /// 20 | /// The ID of an uploaded file that contains validation data. 21 | /// If you provide this file, the data is used to generate validation metrics 22 | /// periodically during fine-tuning. These metrics can be viewed in the fine-tuning results file. 23 | /// Your train and validation data should be mutually exclusive. 24 | /// Your dataset must be formatted as a JSONL file, where each validation example is a 25 | /// JSON object with the keys "prompt" and "completion". 26 | /// Additionally, you must upload your file with the purpose fine-tune. 27 | /// See the fine-tuning guide for more details. 28 | /// 29 | function ValidationFile(const Value: string): TFineTuneCreateParams; 30 | /// 31 | /// The name of the base model to fine-tune. You can select one of "ada", "babbage", "curie", "davinci", or a 32 | /// fine-tuned model created after 2022-04-21. To learn more about these models, see the Models documentation. 33 | /// 34 | function Model(const Value: string): TFineTuneCreateParams; 35 | /// 36 | /// The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset. 37 | /// 38 | function nEpochs(const Value: Integer = 4): TFineTuneCreateParams; 39 | /// 40 | /// The batch size to use for training. The batch size is the number of training examples used to train a 41 | /// single forward and backward pass. 42 | /// By default, the batch size will be dynamically configured to be ~0.2% of the number of examples in the 43 | /// training set, capped at 256 - in general, we've found that larger batch sizes tend to work better for larger datasets. 44 | /// 45 | function BatchSize(const Value: Integer): TFineTuneCreateParams; 46 | /// 47 | /// The learning rate multiplier to use for training. The fine-tuning learning rate is the original 48 | /// learning rate used for pretraining multiplied by this value. 49 | /// By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 depending on 50 | /// final batch_size (larger learning rates tend to perform better with larger batch sizes). 51 | /// We recommend experimenting with values in the range 0.02 to 0.2 to see what produces the best results. 52 | /// 53 | function LearningRateMultiplier(const Value: Extended): TFineTuneCreateParams; 54 | /// 55 | /// The weight to use for loss on the prompt tokens. This controls how much the model tries to 56 | /// learn to generate the prompt (as compared to the completion which always has a weight of 1.0), 57 | /// and can add a stabilizing effect to training when completions are short. 58 | /// If prompts are extremely long (relative to completions), it may make sense to reduce this weight so as 59 | /// to avoid over-prioritizing learning the prompt. 60 | /// 61 | function PromptLossWeight(const Value: Extended = 0.01): TFineTuneCreateParams; 62 | /// 63 | /// If set, we calculate classification-specific metrics such as accuracy and F-1 score using the 64 | /// validation set at the end of every epoch. These metrics can be viewed in the results file. 65 | /// In order to compute classification metrics, you must provide a validation_file. 66 | /// Additionally, you must specify classification_n_classes for multiclass classification or 67 | /// classification_positive_class for binary classification. 68 | /// 69 | function ComputeClassificationMetrics(const Value: Boolean = True): TFineTuneCreateParams; 70 | /// 71 | /// The number of classes in a classification task. 72 | /// This parameter is required for multiclass classification. 73 | /// 74 | function ClassificationNClasses(const Value: Integer): TFineTuneCreateParams; 75 | /// 76 | /// The positive class in binary classification. 77 | /// This parameter is needed to generate precision, recall, and F1 metrics when doing binary classification. 78 | /// 79 | function ClassificationPositiveClass(const Value: string): TFineTuneCreateParams; 80 | /// 81 | /// If this is provided, we calculate F-beta scores at the specified beta values. 82 | /// The F-beta score is a generalization of F-1 score. This is only used for binary classification. 83 | /// With a beta of 1 (i.e. the F-1 score), precision and recall are given the same weight. 84 | /// A larger beta score puts more weight on recall and less on precision. 85 | /// A smaller beta score puts more weight on precision and less on recall. 86 | /// 87 | function ClassificationBetas(const Value: TArray): TFineTuneCreateParams; 88 | /// 89 | /// A string of up to 40 characters that will be added to your fine-tuned model name. 90 | /// For example, a suffix of "custom-model-name" would produce a model name like 91 | /// ada:ft-your-org:custom-model-name-2022-02-15-04-21-04. 92 | /// 93 | function Suffix(const Value: string): TFineTuneCreateParams; 94 | end; 95 | 96 | THyperparams = class 97 | private 98 | FBatch_size: Extended; 99 | FLearning_rate_multiplier: Extended; 100 | FN_epochs: Extended; 101 | FPrompt_loss_weight: Extended; 102 | public 103 | property BatchSize: Extended read FBatch_size write FBatch_size; 104 | property LearningRateMultiplier: Extended read FLearning_rate_multiplier write FLearning_rate_multiplier; 105 | property nEpochs: Extended read FN_epochs write FN_epochs; 106 | property PromptLossWeight: Extended read FPrompt_loss_weight write FPrompt_loss_weight; 107 | end; 108 | 109 | TFineTuneEvent = class 110 | private 111 | FCreated_at: Int64; 112 | FLevel: string; 113 | FMessage: string; 114 | FObject: string; 115 | public 116 | property CreatedAt: Int64 read FCreated_at write FCreated_at; 117 | property Level: string read FLevel write FLevel; 118 | property Message: string read FMessage write FMessage; 119 | property &Object: string read FObject write FObject; 120 | end; 121 | 122 | TFineTuneEvents = class 123 | private 124 | FObject: string; 125 | FData: TArray; 126 | public 127 | property &Object: string read FObject write FObject; 128 | property Data: TArray read FData write FData; 129 | destructor Destroy; override; 130 | end; 131 | 132 | TFineTune = class 133 | private 134 | FCreated_at: Int64; 135 | FEvents: TArray; 136 | FHyperparams: THyperparams; 137 | FId: string; 138 | FModel: string; 139 | FObject: string; 140 | FOrganization_id: string; 141 | FResult_files: TArray; 142 | FStatus: string; 143 | FTraining_files: TArray; 144 | FUpdated_at: Int64; 145 | FValidation_files: TArray; 146 | FFine_tuned_model: string; 147 | public 148 | property CreatedAt: Int64 read FCreated_at write FCreated_at; 149 | property Events: TArray read FEvents write FEvents; 150 | property Hyperparams: THyperparams read FHyperparams write FHyperparams; 151 | property FineTunedModel: string read FFine_tuned_model write FFine_tuned_model; 152 | property Id: string read FId write FId; 153 | property Model: string read FModel write FModel; 154 | property &Object: string read FObject write FObject; 155 | property OrganizationId: string read FOrganization_id write FOrganization_id; 156 | property ResultFiles: TArray read FResult_files write FResult_files; 157 | /// 158 | /// succeeded, cancelled, pending 159 | /// 160 | property Status: string read FStatus write FStatus; 161 | property TrainingFiles: TArray read FTraining_files write FTraining_files; 162 | property UpdatedAt: Int64 read FUpdated_at write FUpdated_at; 163 | property ValidationFiles: TArray read FValidation_files write FValidation_files; 164 | destructor Destroy; override; 165 | end; 166 | 167 | TFineTunes = class 168 | private 169 | FObject: string; 170 | FData: TArray; 171 | public 172 | property &Object: string read FObject write FObject; 173 | property Data: TArray read FData write FData; 174 | destructor Destroy; override; 175 | end; 176 | 177 | TFineTunesRoute = class(TOpenAIAPIRoute) 178 | public 179 | /// 180 | /// Creates a job that fine-tunes a specified model from a given dataset. 181 | /// Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. 182 | /// 183 | function Create(ParamProc: TProc): TFineTune; deprecated 'Use FineTuning'; 184 | /// 185 | /// List your organization's fine-tuning jobs 186 | /// 187 | function List: TFineTunes; deprecated 'Use FineTuning'; 188 | /// 189 | /// Gets info about the fine-tune job. 190 | /// 191 | function Retrieve(const FineTuneId: string): TFineTune; deprecated 'Use FineTuning'; 192 | /// 193 | /// Immediately cancel a fine-tune job. 194 | /// 195 | function Cancel(const FineTuneId: string): TFineTune; deprecated 'Use FineTuning'; 196 | /// 197 | /// Get fine-grained status updates for a fine-tune job. 198 | /// 199 | /// Id of FineTune 200 | /// Whether to stream events for the fine-tune job. 201 | /// If set to true, events will be sent as data-only server-sent events as they become available. 202 | /// The stream will terminate with a data: [DONE] message when the job is finished (succeeded, cancelled, or failed). 203 | /// If set to false, only events generated so far will be returned. 204 | /// 205 | function ListEvents(const FineTuneId: string; Stream: Boolean = False): TFineTuneEvents; deprecated 'Use FineTuning'; 206 | /// 207 | /// Delete a fine-tuned model. You must have the Owner role in your organization. 208 | /// 209 | function Delete(const Model: string): TDeletionStatus; deprecated 'Use FineTuning'; 210 | end; 211 | 212 | implementation 213 | 214 | uses 215 | System.StrUtils; 216 | 217 | { TFineTunesRoute } 218 | 219 | function TFineTunesRoute.Cancel(const FineTuneId: string): TFineTune; 220 | begin 221 | Result := API.Post('fine-tunes/' + FineTuneId + '/cancel'); 222 | end; 223 | 224 | function TFineTunesRoute.Create(ParamProc: TProc): TFineTune; 225 | begin 226 | Result := API.Post('fine-tunes', ParamProc); 227 | end; 228 | 229 | function TFineTunesRoute.Delete(const Model: string): TDeletionStatus; 230 | begin 231 | Result := API.Delete('models/' + Model); 232 | end; 233 | 234 | function TFineTunesRoute.List: TFineTunes; 235 | begin 236 | Result := API.Get('fine-tunes'); 237 | end; 238 | 239 | function TFineTunesRoute.ListEvents(const FineTuneId: string; Stream: Boolean): TFineTuneEvents; 240 | begin 241 | Result := API.Get('fine-tunes/' + FineTuneId + '/events' + IfThen(Stream, '?stream=true')); 242 | end; 243 | 244 | function TFineTunesRoute.Retrieve(const FineTuneId: string): TFineTune; 245 | begin 246 | Result := API.Get('fine-tunes/' + FineTuneId); 247 | end; 248 | 249 | { TFineTune } 250 | 251 | destructor TFineTune.Destroy; 252 | var 253 | Event: TFineTuneEvent; 254 | AFile: TFile; 255 | begin 256 | if Assigned(FHyperparams) then 257 | FHyperparams.Free; 258 | for Event in FEvents do 259 | if Assigned(Event) then 260 | Event.Free; 261 | for AFile in FResult_files do 262 | if Assigned(AFile) then 263 | AFile.Free; 264 | for AFile in FTraining_files do 265 | if Assigned(AFile) then 266 | AFile.Free; 267 | for AFile in FValidation_files do 268 | if Assigned(AFile) then 269 | AFile.Free; 270 | inherited; 271 | end; 272 | 273 | { TFineTunes } 274 | 275 | destructor TFineTunes.Destroy; 276 | var 277 | Item: TFineTune; 278 | begin 279 | for Item in FData do 280 | if Assigned(Item) then 281 | Item.Free; 282 | inherited; 283 | end; 284 | 285 | { TFineTuneEvents } 286 | 287 | destructor TFineTuneEvents.Destroy; 288 | var 289 | Item: TFineTuneEvent; 290 | begin 291 | for Item in FData do 292 | if Assigned(Item) then 293 | Item.Free; 294 | inherited; 295 | end; 296 | 297 | { TFineTuneCreateParams } 298 | 299 | function TFineTuneCreateParams.BatchSize(const Value: Integer): TFineTuneCreateParams; 300 | begin 301 | Result := TFineTuneCreateParams(Add('batch_size', Value)); 302 | end; 303 | 304 | function TFineTuneCreateParams.ClassificationBetas(const Value: TArray): TFineTuneCreateParams; 305 | begin 306 | Result := TFineTuneCreateParams(Add('classification_betas', Value)); 307 | end; 308 | 309 | function TFineTuneCreateParams.ClassificationNClasses(const Value: Integer): TFineTuneCreateParams; 310 | begin 311 | Result := TFineTuneCreateParams(Add('classification_n_classes', Value)); 312 | end; 313 | 314 | function TFineTuneCreateParams.ClassificationPositiveClass(const Value: string): TFineTuneCreateParams; 315 | begin 316 | Result := TFineTuneCreateParams(Add('classification_positive_class', Value)); 317 | end; 318 | 319 | function TFineTuneCreateParams.ComputeClassificationMetrics(const Value: Boolean): TFineTuneCreateParams; 320 | begin 321 | Result := TFineTuneCreateParams(Add('compute_classification_metrics', Value)); 322 | end; 323 | 324 | function TFineTuneCreateParams.LearningRateMultiplier(const Value: Extended): TFineTuneCreateParams; 325 | begin 326 | Result := TFineTuneCreateParams(Add('learning_rate_multiplier', Value)); 327 | end; 328 | 329 | function TFineTuneCreateParams.Model(const Value: string): TFineTuneCreateParams; 330 | begin 331 | Result := TFineTuneCreateParams(Add('model', Value)); 332 | end; 333 | 334 | function TFineTuneCreateParams.nEpochs(const Value: Integer): TFineTuneCreateParams; 335 | begin 336 | Result := TFineTuneCreateParams(Add('n_epochs', Value)); 337 | end; 338 | 339 | function TFineTuneCreateParams.PromptLossWeight(const Value: Extended): TFineTuneCreateParams; 340 | begin 341 | Result := TFineTuneCreateParams(Add('prompt_loss_weight', Value)); 342 | end; 343 | 344 | function TFineTuneCreateParams.Suffix(const Value: string): TFineTuneCreateParams; 345 | begin 346 | Result := TFineTuneCreateParams(Add('suffix', Value)); 347 | end; 348 | 349 | function TFineTuneCreateParams.TrainingFile(const Value: string): TFineTuneCreateParams; 350 | begin 351 | Result := TFineTuneCreateParams(Add('training_file', Value)); 352 | end; 353 | 354 | function TFineTuneCreateParams.ValidationFile(const Value: string): TFineTuneCreateParams; 355 | begin 356 | Result := TFineTuneCreateParams(Add('validation_file', Value)); 357 | end; 358 | 359 | end. 360 | 361 | -------------------------------------------------------------------------------- /OpenAI.Audio.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Audio; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.SysUtils, System.Net.Mime, OpenAI.API.Params, 7 | OpenAI.API; 8 | 9 | {$SCOPEDENUMS ON} 10 | 11 | type 12 | TAudioResponseFormat = (Json, Text, Srt, VerboseJson, Vtt); 13 | 14 | TAudioResponseFormatHelper = record helper for TAudioResponseFormat 15 | function ToString: string; 16 | end; 17 | 18 | TAudioFileResponseFormat = (MP3, OPUS, AAC, FLAC, WAV, PCM); 19 | 20 | TAudioFileResponseFormatHelper = record helper for TAudioFileResponseFormat 21 | function ToString: string; 22 | end; 23 | 24 | TAudioTranscription = class(TMultipartFormData) 25 | /// 26 | /// Required. 27 | /// The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. 28 | /// 29 | function &File(const FileName: TFileName): TAudioTranscription; overload; 30 | /// 31 | /// Required. 32 | /// The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. 33 | /// 34 | function &File(const Stream: TStream; const FileName: TFileName): TAudioTranscription; overload; 35 | /// 36 | /// Required. 37 | /// ID of the model to use. Only whisper-1 is currently available. 38 | /// 39 | function Model(const Value: string): TAudioTranscription; overload; 40 | /// 41 | /// An optional text to guide the model's style or continue a previous audio segment. 42 | /// The prompt should match the audio language. 43 | /// 44 | /// https://platform.openai.com/docs/guides/speech-to-text/prompting 45 | function Prompt(const Value: string): TAudioTranscription; overload; 46 | /// 47 | /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. 48 | /// 49 | function ResponseFormat(const Value: string): TAudioTranscription; overload; 50 | /// 51 | /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. 52 | /// 53 | function ResponseFormat(const Value: TAudioResponseFormat): TAudioTranscription; overload; 54 | /// 55 | /// The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, 56 | /// while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use 57 | /// log probability to automatically increase the temperature until certain thresholds are hit. 58 | /// 59 | function Temperature(const Value: Single = 0): TAudioTranscription; 60 | /// 61 | /// The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency (like en, ru, uk). 62 | /// 63 | function Language(const Value: string): TAudioTranscription; overload; 64 | /// 65 | /// The timestamp granularities to populate for this transcription. 66 | /// response_format must be set verbose_json to use timestamp granularities. 67 | /// Either or both of these options are supported: word, or segment. 68 | /// Note: There is no additional latency for segment timestamps, 69 | /// but generating word timestamps incurs additional latency. 70 | /// 71 | function TimestampGranularities(const Value: string): TAudioTranscription; overload; 72 | constructor Create; reintroduce; 73 | end; 74 | 75 | TAudioTranslation = class(TMultipartFormData) 76 | /// 77 | /// Required. 78 | /// The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. 79 | /// 80 | function &File(const FileName: TFileName): TAudioTranslation; overload; 81 | /// 82 | /// Required. 83 | /// The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. 84 | /// 85 | function &File(const Stream: TStream; const FileName: TFileName): TAudioTranslation; overload; 86 | /// 87 | /// Required. 88 | /// ID of the model to use. Only whisper-1 (which is powered by our open source Whisper V2 model) is currently available. 89 | /// 90 | function Model(const Value: string): TAudioTranslation; overload; 91 | /// 92 | /// An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. 93 | /// 94 | /// https://platform.openai.com/docs/guides/speech-to-text/prompting 95 | function Prompt(const Value: string): TAudioTranslation; overload; 96 | /// 97 | /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. 98 | /// 99 | function ResponseFormat(const Value: string): TAudioTranslation; overload; 100 | /// 101 | /// The format of the transcript output, in one of these options: json, text, srt, verbose_json, or vtt. 102 | /// 103 | function ResponseFormat(const Value: TAudioResponseFormat): TAudioTranslation; overload; 104 | /// 105 | /// The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, 106 | /// while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use 107 | /// log probability to automatically increase the temperature until certain thresholds are hit. 108 | /// 109 | function Temperature(const Value: Single): TAudioTranslation; 110 | constructor Create; reintroduce; 111 | end; 112 | 113 | TAudioSpeechParams = class(TJSONParam) 114 | /// 115 | /// One of the available TTS models: tts-1 or tts-1-hd 116 | /// 117 | /// https://platform.openai.com/docs/models/tts 118 | function Model(const Value: string): TAudioSpeechParams; 119 | /// 120 | /// The text to generate audio for. The maximum length is 4096 characters. 121 | /// 122 | function Input(const Value: string): TAudioSpeechParams; overload; 123 | /// 124 | /// The voice to use when generating the audio. 125 | /// Supported voices are alloy, echo, fable, onyx, nova, and shimmer. 126 | /// Previews of the voices are available in the Text to speech guide. 127 | /// 128 | /// https://platform.openai.com/docs/guides/text-to-speech/voice-options 129 | function Voice(const Value: string): TAudioSpeechParams; overload; 130 | /// 131 | /// The format to audio in. Supported formats are mp3, opus, aac, flac, wav, and pcm. 132 | /// 133 | function ResponseFormat(const Value: string): TAudioSpeechParams; overload; 134 | /// 135 | /// The format to audio in. Supported formats are mp3, opus, aac, flac, wav, and pcm. 136 | /// 137 | function ResponseFormat(const Value: TAudioFileResponseFormat): TAudioSpeechParams; overload; 138 | /// 139 | /// The speed of the generated audio. Select a value from 0.25 to 4.0. 1.0 is the default. 140 | /// 141 | function Speed(const Value: Single): TAudioSpeechParams; 142 | constructor Create; override; 143 | end; 144 | 145 | /// 146 | /// Represents a transcription response returned by model, based on the provided input. 147 | /// 148 | TAudioText = class 149 | private 150 | FText: string; 151 | public 152 | property Text: string read FText write FText; 153 | end; 154 | 155 | TAudioTranscriptionWord = class 156 | private 157 | FWord: string; 158 | FStart: Extended; 159 | FEnd: Extended; 160 | public 161 | /// 162 | /// The text content of the word. 163 | /// 164 | property Word: string read FWord write FWord; 165 | /// 166 | /// Start time of the word in seconds. 167 | /// 168 | property Start: Extended read FStart write FStart; 169 | /// 170 | /// End time of the word in seconds. 171 | /// 172 | property &End: Extended read FEnd write FEnd; 173 | end; 174 | 175 | TAudioTranscriptionSegment = class 176 | private 177 | FId: Int64; 178 | FSeek: Int64; 179 | FStart: Extended; 180 | FEnd: Extended; 181 | FText: string; 182 | FTokens: TArray; 183 | FTemperature: Extended; 184 | FAvg_logprob: Extended; 185 | FCompression_ratio: Extended; 186 | FNo_speech_prob: Extended; 187 | public 188 | /// 189 | /// Unique identifier of the segment. 190 | /// 191 | property Id: Int64 read FId write FId; 192 | /// 193 | /// Seek offset of the segment. 194 | /// 195 | property Seek: Int64 read FSeek write FSeek; 196 | /// 197 | /// Start time of the segment in seconds. 198 | /// 199 | property Start: Extended read FStart write FStart; 200 | /// 201 | /// End time of the segment in seconds. 202 | /// 203 | property &End: Extended read FEnd write FEnd; 204 | /// 205 | /// Text content of the segment. 206 | /// 207 | property Text: string read FText write FText; 208 | /// 209 | /// Array of token IDs for the text content. 210 | /// 211 | property Tokens: TArray read FTokens write FTokens; 212 | /// 213 | /// Temperature parameter used for generating the segment. 214 | /// 215 | property Temperature: Extended read FTemperature write FTemperature; 216 | /// 217 | /// Average logprob of the segment. If the value is lower than -1, consider the logprobs failed. 218 | /// 219 | property AVGLogprob: Extended read FAvg_logprob write FAvg_logprob; 220 | /// 221 | /// Compression ratio of the segment. If the value is greater than 2.4, consider the compression failed. 222 | /// 223 | property CompressionRatio: Extended read FCompression_ratio write FCompression_ratio; 224 | /// 225 | /// Probability of no speech in the segment. If the value is higher than 1.0 and 226 | /// the avg_logprob is below -1, consider this segment silent. 227 | /// 228 | property NoSpeechProb: Extended read FNo_speech_prob write FNo_speech_prob; 229 | end; 230 | 231 | /// 232 | /// Represents a transcription response returned by model, based on the provided input. 233 | /// 234 | TAudioTranscriptionObject = class 235 | private 236 | FText: string; 237 | FLanguage: string; 238 | FDuration: string; 239 | FWords: TArray; 240 | FSegments: TArray; 241 | public 242 | /// 243 | /// The transcribed text. 244 | /// 245 | property Text: string read FText write FText; 246 | /// 247 | /// The language of the input audio. 248 | /// 249 | property Language: string read FLanguage write FLanguage; 250 | /// 251 | /// The duration of the input audio. 252 | /// 253 | property Duration: string read FDuration write FDuration; 254 | /// 255 | /// Extracted words and their corresponding timestamps. 256 | /// 257 | property Words: TArray read FWords write FWords; 258 | /// 259 | /// Segments of the transcribed text and their corresponding details. 260 | /// 261 | property Segments: TArray read FSegments write FSegments; 262 | destructor Destroy; override; 263 | end; 264 | 265 | /// 266 | /// Learn how to turn audio into text or text into audio. 267 | /// 268 | TAudioRoute = class(TOpenAIAPIRoute) 269 | public 270 | /// 271 | /// Transcribes audio into the input language. 272 | /// 273 | function CreateTranscription(ParamProc: TProc): TAudioTranscriptionObject; 274 | /// 275 | /// Translates audio into into English. 276 | /// 277 | function CreateTranslation(ParamProc: TProc): TAudioText; 278 | /// 279 | /// Generates audio from the input text. 280 | /// 281 | procedure CreateSpeech(ParamProc: TProc; Stream: TStream); 282 | end; 283 | 284 | implementation 285 | 286 | { TAudioRoute } 287 | 288 | procedure TAudioRoute.CreateSpeech(ParamProc: TProc; Stream: TStream); 289 | begin 290 | API.Post('audio/speech', ParamProc, Stream); 291 | end; 292 | 293 | function TAudioRoute.CreateTranscription(ParamProc: TProc): TAudioTranscriptionObject; 294 | begin 295 | Result := API.PostForm('audio/transcriptions', ParamProc); 296 | end; 297 | 298 | function TAudioRoute.CreateTranslation(ParamProc: TProc): TAudioText; 299 | begin 300 | Result := API.PostForm('audio/translations', ParamProc); 301 | end; 302 | 303 | { TAudioTranscription } 304 | 305 | function TAudioTranscription.&File(const FileName: TFileName): TAudioTranscription; 306 | begin 307 | AddFile('file', FileName); 308 | Result := Self; 309 | end; 310 | 311 | constructor TAudioTranscription.Create; 312 | begin 313 | inherited Create(True); 314 | Model('whisper-1'); 315 | end; 316 | 317 | function TAudioTranscription.&File(const Stream: TStream; const FileName: TFileName): TAudioTranscription; 318 | begin 319 | {$IF CompilerVersion <= 35} 320 | AddStream('file', Stream, FileName); 321 | {$ELSE} 322 | AddStream('file', Stream, False, FileName); 323 | {$ENDIF} 324 | Result := Self; 325 | end; 326 | 327 | function TAudioTranscription.Language(const Value: string): TAudioTranscription; 328 | begin 329 | AddField('language', Value); 330 | Result := Self; 331 | end; 332 | 333 | function TAudioTranscription.Temperature(const Value: Single): TAudioTranscription; 334 | begin 335 | AddField('temperature', FormatFloat('0,0', Value)); 336 | Result := Self; 337 | end; 338 | 339 | function TAudioTranscription.TimestampGranularities(const Value: string): TAudioTranscription; 340 | begin 341 | AddField('timestamp_granularities[]', Value); 342 | Result := Self; 343 | end; 344 | 345 | function TAudioTranscription.Prompt(const Value: string): TAudioTranscription; 346 | begin 347 | AddField('prompt', Value); 348 | Result := Self; 349 | end; 350 | 351 | function TAudioTranscription.ResponseFormat(const Value: TAudioResponseFormat): TAudioTranscription; 352 | begin 353 | Result := ResponseFormat(Value.ToString); 354 | end; 355 | 356 | function TAudioTranscription.ResponseFormat(const Value: string): TAudioTranscription; 357 | begin 358 | AddField('response_format', Value); 359 | Result := Self; 360 | end; 361 | 362 | function TAudioTranscription.Model(const Value: string): TAudioTranscription; 363 | begin 364 | AddField('model', Value); 365 | Result := Self; 366 | end; 367 | 368 | { TAudioTranslation } 369 | 370 | function TAudioTranslation.&File(const FileName: TFileName): TAudioTranslation; 371 | begin 372 | AddFile('file', FileName); 373 | Result := Self; 374 | end; 375 | 376 | constructor TAudioTranslation.Create; 377 | begin 378 | inherited Create(True); 379 | end; 380 | 381 | function TAudioTranslation.&File(const Stream: TStream; const FileName: TFileName): TAudioTranslation; 382 | begin 383 | {$IF CompilerVersion <= 35} 384 | AddStream('file', Stream, FileName); 385 | {$ELSE} 386 | AddStream('file', Stream, False, FileName); 387 | {$ENDIF} 388 | Result := Self; 389 | end; 390 | 391 | function TAudioTranslation.Temperature(const Value: Single): TAudioTranslation; 392 | begin 393 | AddField('temperature', FormatFloat('0,0', Value)); 394 | Result := Self; 395 | end; 396 | 397 | function TAudioTranslation.Prompt(const Value: string): TAudioTranslation; 398 | begin 399 | AddField('prompt', Value); 400 | Result := Self; 401 | end; 402 | 403 | function TAudioTranslation.ResponseFormat(const Value: TAudioResponseFormat): TAudioTranslation; 404 | begin 405 | AddField('response_format', Value.ToString); 406 | Result := Self; 407 | end; 408 | 409 | function TAudioTranslation.ResponseFormat(const Value: string): TAudioTranslation; 410 | begin 411 | AddField('response_format', Value); 412 | Result := Self; 413 | end; 414 | 415 | function TAudioTranslation.Model(const Value: string): TAudioTranslation; 416 | begin 417 | AddField('model', Value); 418 | Result := Self; 419 | end; 420 | 421 | { TAudioResponseFormatHelper } 422 | 423 | function TAudioResponseFormatHelper.ToString: string; 424 | begin 425 | case Self of 426 | TAudioResponseFormat.Json: 427 | Result := 'json'; 428 | TAudioResponseFormat.Text: 429 | Result := 'text'; 430 | TAudioResponseFormat.Srt: 431 | Result := 'srt'; 432 | TAudioResponseFormat.VerboseJson: 433 | Result := 'verbose_json'; 434 | TAudioResponseFormat.Vtt: 435 | Result := 'vtt'; 436 | end; 437 | end; 438 | 439 | { TAudioSpeechParams } 440 | 441 | constructor TAudioSpeechParams.Create; 442 | begin 443 | inherited; 444 | Model('tts-1'); 445 | Voice('alloy'); 446 | end; 447 | 448 | function TAudioSpeechParams.Input(const Value: string): TAudioSpeechParams; 449 | begin 450 | Result := TAudioSpeechParams(Add('input', Value)); 451 | end; 452 | 453 | function TAudioSpeechParams.Model(const Value: string): TAudioSpeechParams; 454 | begin 455 | Result := TAudioSpeechParams(Add('model', Value)); 456 | end; 457 | 458 | function TAudioSpeechParams.ResponseFormat(const Value: TAudioFileResponseFormat): TAudioSpeechParams; 459 | begin 460 | Result := TAudioSpeechParams(Add('response_format', Value.ToString)); 461 | end; 462 | 463 | function TAudioSpeechParams.ResponseFormat(const Value: string): TAudioSpeechParams; 464 | begin 465 | Result := TAudioSpeechParams(Add('response_format', Value)); 466 | end; 467 | 468 | function TAudioSpeechParams.Speed(const Value: Single): TAudioSpeechParams; 469 | begin 470 | Result := TAudioSpeechParams(Add('speed', Value)); 471 | end; 472 | 473 | function TAudioSpeechParams.Voice(const Value: string): TAudioSpeechParams; 474 | begin 475 | Result := TAudioSpeechParams(Add('voice', Value)); 476 | end; 477 | 478 | { TAudioFileResponseFormatHelper } 479 | 480 | function TAudioFileResponseFormatHelper.ToString: string; 481 | begin 482 | case Self of 483 | TAudioFileResponseFormat.MP3: 484 | Exit('mp3'); 485 | TAudioFileResponseFormat.OPUS: 486 | Exit('opus'); 487 | TAudioFileResponseFormat.AAC: 488 | Exit('aac'); 489 | TAudioFileResponseFormat.FLAC: 490 | Exit('flac'); 491 | TAudioFileResponseFormat.WAV: 492 | Exit('wav'); 493 | TAudioFileResponseFormat.PCM: 494 | Exit('pcm'); 495 | else 496 | Exit('mp3'); 497 | end; 498 | end; 499 | 500 | { TAudioTranscriptionObject } 501 | 502 | destructor TAudioTranscriptionObject.Destroy; 503 | var 504 | AWord: TAudioTranscriptionWord; 505 | ASegment: TAudioTranscriptionSegment; 506 | begin 507 | for AWord in FWords do 508 | AWord.Free; 509 | for ASegment in FSegments do 510 | ASegment.Free; 511 | inherited; 512 | end; 513 | 514 | end. 515 | 516 | -------------------------------------------------------------------------------- /OpenAI.Component.Chat.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.Component.Chat; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, System.SyncObjs, System.Generics.Collections, 7 | System.Threading, OpenAI, OpenAI.Chat, OpenAI.Component.Functions, 8 | OpenAI.Utils.ObjectHolder, OpenAI.Component.ChatHistory; 9 | 10 | type 11 | TChat = OpenAI.Chat.TChat; 12 | 13 | TChatMessageBuild = OpenAI.Chat.TChatMessageBuild; 14 | 15 | TOnChat = procedure(Sender: TObject; Event: TChat) of object; 16 | 17 | TOnChatDelta = procedure(Sender: TObject; Event: TChat; IsDone: Boolean) of object; 18 | 19 | TOnFunctionCall = procedure(Sender: TObject; const FuncName, FuncArgs: string) of object; 20 | 21 | TOnError = procedure(Sender: TObject; Error: Exception) of object; 22 | 23 | ExceptionChatBusy = class(Exception); 24 | 25 | TOpenAIChatCustom = class(TComponent) 26 | private 27 | FClient: TOpenAIClient; 28 | FTask: ITask; 29 | FModel: string; 30 | FN: Integer; 31 | FPresencePenalty: Single; 32 | FStream: Boolean; 33 | FMaxTokens: Integer; 34 | FStop: TStringList; 35 | FTopP: Single; 36 | FTemperature: Single; 37 | FUser: string; 38 | FFrequencyPenalty: Single; 39 | FOnChat: TOnChat; 40 | FSynchronize: Boolean; 41 | FOnError: TOnError; 42 | FOnBeginWork: TNotifyEvent; 43 | FOnEndWork: TNotifyEvent; 44 | FOnChatDelta: TOnChatDelta; 45 | FDoStreamStop: Boolean; 46 | FFunctions: TOpenAIChatFunctions; 47 | FOnFunctionCall: TOnFunctionCall; 48 | FHistory: TOpenAIChatHistoryCustom; 49 | procedure SetClient(const Value: TOpenAIClient); 50 | procedure SetModel(const Value: string); 51 | procedure SetFrequencyPenalty(const Value: Single); 52 | procedure SetMaxTokens(const Value: Integer); 53 | procedure SetN(const Value: Integer); 54 | procedure SetPresencePenalty(const Value: Single); 55 | procedure SetStream(const Value: Boolean); 56 | procedure SetTemperature(const Value: Single); 57 | procedure SetTopP(const Value: Single); 58 | procedure SetUser(const Value: string); 59 | procedure SetOnChat(const Value: TOnChat); 60 | procedure CheckTask; 61 | procedure SetSynchronize(const Value: Boolean); 62 | procedure SetOnError(const Value: TOnError); 63 | procedure InternalSend(const Messages: TArray); 64 | procedure InternalStreamSend(const Messages: TArray); 65 | function GetIsBusy: Boolean; 66 | procedure SetOnBeginWork(const Value: TNotifyEvent); 67 | procedure SetOnEndWork(const Value: TNotifyEvent); 68 | procedure SetOnChatDelta(const Value: TOnChatDelta); 69 | procedure SetFunctions(const Value: TOpenAIChatFunctions); 70 | procedure SetOnFunctionCall(const Value: TOnFunctionCall); 71 | procedure DoFunctionCall(ChatFunctions: TArray); 72 | procedure SetHistory(const Value: TOpenAIChatHistoryCustom); 73 | protected 74 | procedure DoChat(Chat: TChat); virtual; 75 | procedure DoChatDelta(Chat: TChat; IsDone: Boolean); virtual; 76 | procedure DoError(E: Exception); virtual; 77 | procedure DoBeginWork; virtual; 78 | procedure DoEndWork; virtual; 79 | public 80 | constructor Create(AOwner: TComponent); override; 81 | destructor Destroy; override; 82 | public 83 | property Client: TOpenAIClient read FClient write SetClient; 84 | property Functions: TOpenAIChatFunctions read FFunctions write SetFunctions; 85 | property History: TOpenAIChatHistoryCustom read FHistory write SetHistory; 86 | property Model: string read FModel write SetModel; 87 | property Temperature: Single read FTemperature write SetTemperature; 88 | property TopP: Single read FTopP write SetTopP; 89 | property N: Integer read FN write SetN; 90 | property Stream: Boolean read FStream write SetStream; 91 | property Stop: TStringList read FStop; 92 | property MaxTokens: Integer read FMaxTokens write SetMaxTokens; 93 | property PresencePenalty: Single read FPresencePenalty write SetPresencePenalty; 94 | property FrequencyPenalty: Single read FFrequencyPenalty write SetFrequencyPenalty; 95 | property User: string read FUser write SetUser; 96 | property Synchronize: Boolean read FSynchronize write SetSynchronize; 97 | public 98 | property IsBusy: Boolean read GetIsBusy; 99 | public 100 | procedure Send(const Messages: TArray); 101 | procedure StopStream; 102 | public 103 | property OnChat: TOnChat read FOnChat write SetOnChat; 104 | property OnChatDelta: TOnChatDelta read FOnChatDelta write SetOnChatDelta; 105 | property OnFunctionCall: TOnFunctionCall read FOnFunctionCall write SetOnFunctionCall; 106 | property OnError: TOnError read FOnError write SetOnError; 107 | property OnBeginWork: TNotifyEvent read FOnBeginWork write SetOnBeginWork; 108 | property OnEndWork: TNotifyEvent read FOnEndWork write SetOnEndWork; 109 | end; 110 | 111 | TOpenAIChat = class(TOpenAIChatCustom) 112 | published 113 | property Client; 114 | property History; 115 | property Functions; 116 | /// 117 | /// ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API. 118 | /// 119 | /// https://platform.openai.com/docs/models/model-endpoint-compatibility 120 | property Model; 121 | /// 122 | /// What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, 123 | /// while lower values like 0.2 will make it more focused and deterministic. 124 | /// We generally recommend altering this or top_p but not both. 125 | /// 126 | property Temperature; 127 | /// 128 | /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the 129 | /// results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% 130 | /// probability mass are considered. 131 | /// We generally recommend altering this or temperature but not both. 132 | /// 133 | property TopP; 134 | /// 135 | /// How many chat completion choices to generate for each input message. 136 | /// 137 | property N default 1; 138 | /// 139 | /// If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as 140 | /// data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message. 141 | /// 142 | property Stream default False; 143 | /// 144 | /// Up to 4 sequences where the API will stop generating further tokens. 145 | /// 146 | property Stop; 147 | /// 148 | /// The maximum number of tokens allowed for the generated answer. By default, the number of 149 | /// tokens the model can return will be (4096 - prompt tokens). 150 | /// 151 | property MaxTokens default 1024; 152 | /// 153 | /// Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, 154 | /// increasing the model's likelihood to talk about new topics. 155 | /// 156 | property PresencePenalty; 157 | /// 158 | /// Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, 159 | /// decreasing the model's likelihood to repeat the same line verbatim. 160 | /// 161 | property FrequencyPenalty; 162 | /// 163 | /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. 164 | /// 165 | property User; 166 | property Synchronize default True; 167 | published 168 | property OnChat; 169 | property OnChatDelta; 170 | property OnError; 171 | property OnBeginWork; 172 | property OnEndWork; 173 | end; 174 | 175 | implementation 176 | 177 | uses 178 | OpenAI.Chat.Functions; 179 | 180 | { TOpenAIChatCustom } 181 | 182 | procedure TOpenAIChatCustom.CheckTask; 183 | begin 184 | if FTask <> nil then 185 | raise ExceptionChatBusy.Create('Already running'); 186 | end; 187 | 188 | constructor TOpenAIChatCustom.Create(AOwner: TComponent); 189 | begin 190 | inherited; 191 | FStop := TStringList.Create; 192 | FTemperature := 1; 193 | FStream := False; 194 | FModel := 'gpt-4'; 195 | FTopP := 1; 196 | FN := 1; 197 | FMaxTokens := 1024; 198 | FPresencePenalty := 0; 199 | FFrequencyPenalty := 0; 200 | FSynchronize := True; 201 | end; 202 | 203 | destructor TOpenAIChatCustom.Destroy; 204 | begin 205 | FDoStreamStop := True; 206 | if Assigned(FTask) then 207 | begin 208 | var WTask := FTask; 209 | WTask.Wait; 210 | end; 211 | FStop.Free; 212 | inherited; 213 | end; 214 | 215 | procedure TOpenAIChatCustom.DoBeginWork; 216 | begin 217 | if Assigned(FOnBeginWork) then 218 | FOnBeginWork(Self); 219 | end; 220 | 221 | procedure TOpenAIChatCustom.DoFunctionCall(ChatFunctions: TArray); 222 | begin 223 | var FuncMessages: TArray := []; 224 | for var Func in ChatFunctions do 225 | begin 226 | if Assigned(FOnFunctionCall) then 227 | FOnFunctionCall(Self, Func.&Function.Name, Func.&Function.Arguments); 228 | FuncMessages := FuncMessages + 229 | [TChatMessageBuild.Func(FFunctions.Call(Func.&Function.Name, Func.&Function.Arguments), Func.&Function.Name)]; 230 | end; 231 | if Length(FuncMessages) > 0 then 232 | InternalSend(FuncMessages); 233 | end; 234 | 235 | procedure TOpenAIChatCustom.DoChat(Chat: TChat); 236 | begin 237 | if Length(Chat.Choices) > 0 then 238 | if Assigned(Chat.Choices[0].Message) then 239 | if Length(Chat.Choices[0].Message.ToolCalls) > 0 then 240 | begin 241 | DoFunctionCall(Chat.Choices[0].Message.ToolCalls); 242 | Exit; 243 | end; 244 | 245 | if Assigned(FOnChat) then 246 | FOnChat(Self, Chat); 247 | end; 248 | 249 | procedure TOpenAIChatCustom.DoChatDelta(Chat: TChat; IsDone: Boolean); 250 | begin 251 | if Assigned(FOnChatDelta) then 252 | FOnChatDelta(Self, Chat, IsDone); 253 | end; 254 | 255 | procedure TOpenAIChatCustom.DoEndWork; 256 | begin 257 | FTask := nil; 258 | if Assigned(FOnEndWork) then 259 | FOnEndWork(Self); 260 | end; 261 | 262 | procedure TOpenAIChatCustom.DoError(E: Exception); 263 | begin 264 | if Assigned(FOnError) then 265 | FOnError(Self, E); 266 | end; 267 | 268 | function TOpenAIChatCustom.GetIsBusy: Boolean; 269 | begin 270 | Result := FTask <> nil; 271 | end; 272 | 273 | procedure TOpenAIChatCustom.Send(const Messages: TArray); 274 | begin 275 | CheckTask; 276 | if Assigned(FHistory) then 277 | begin 278 | FHistory.Items.AddRange(Messages); 279 | if not Stream then 280 | InternalSend(FHistory.Items.ToArray) 281 | else 282 | InternalStreamSend(FHistory.Items.ToArray); 283 | end 284 | else 285 | begin 286 | if not Stream then 287 | InternalSend(Messages) 288 | else 289 | InternalStreamSend(Messages); 290 | end; 291 | end; 292 | 293 | procedure TOpenAIChatCustom.InternalSend(const Messages: TArray); 294 | begin 295 | var CurrentChat := Self; 296 | var FMessages := Messages; 297 | var FSync := Self.FSynchronize; 298 | var FModel := Self.FModel; 299 | var FTemperature := Self.FTemperature; 300 | var FTopP := Self.FTopP; 301 | var FN := Self.FN; 302 | var FStop := Self.FStop.ToStringArray; 303 | var FMaxTokens := Self.FMaxTokens; 304 | var FPresencePenalty := Self.FPresencePenalty; 305 | var FFrequencyPenalty := Self.FFrequencyPenalty; 306 | var FUser := Self.FUser; 307 | var FChatTools: TArray := []; 308 | 309 | if Assigned(FFunctions) then 310 | for var Item in FFunctions.GetList do 311 | FChatTools := FChatTools + [TChatToolFunctionParam.Create(Item)]; 312 | 313 | FTask := TaskRun(Self, 314 | procedure(Holder: IComponentHolder) 315 | begin 316 | try 317 | var Chat: TChat; 318 | try 319 | Chat := FClient.Chat.Create( 320 | procedure(Params: TChatParams) 321 | begin 322 | Params.Messages(FMessages); 323 | Params.Model(FModel); 324 | Params.Temperature(FTemperature); 325 | Params.TopP(FTopP); 326 | Params.N(FN); 327 | if Length(FStop) > 0 then 328 | Params.Stop(FStop); 329 | Params.MaxTokens(FMaxTokens); 330 | Params.PresencePenalty(FPresencePenalty); 331 | Params.FrequencyPenalty(FFrequencyPenalty); 332 | Params.User(FUser); 333 | if Length(FChatTools) > 0 then 334 | Params.Tools(FChatTools); 335 | end); 336 | if not Holder.IsLive then 337 | begin 338 | Chat.Free; 339 | Exit; 340 | end; 341 | except 342 | on E: Exception do 343 | begin 344 | if FSync then 345 | begin 346 | var Err := AcquireExceptionObject; 347 | Queue( 348 | procedure 349 | begin 350 | try 351 | if Holder.IsLive then 352 | CurrentChat.DoError(E); 353 | finally 354 | Err.Free; 355 | end; 356 | end); 357 | end 358 | else 359 | begin 360 | if Holder.IsLive then 361 | CurrentChat.DoError(E); 362 | end; 363 | Exit; 364 | end; 365 | end; 366 | if FSync then 367 | Queue( 368 | procedure 369 | begin 370 | try 371 | if Holder.IsLive then 372 | CurrentChat.DoChat(Chat); 373 | finally 374 | Chat.Free; 375 | end; 376 | end) 377 | else 378 | try 379 | if Holder.IsLive then 380 | CurrentChat.DoChat(Chat); 381 | finally 382 | Chat.Free; 383 | end; 384 | finally 385 | if Holder.IsLive then 386 | CurrentChat.DoEndWork; 387 | end; 388 | end); 389 | DoBeginWork; 390 | FTask.Start; 391 | end; 392 | 393 | procedure TOpenAIChatCustom.InternalStreamSend(const Messages: TArray); 394 | begin 395 | var CurrentChat := Self; 396 | var FMessages := Messages; 397 | var FSync := Self.FSynchronize; 398 | var FModel := Self.FModel; 399 | var FTemperature := Self.FTemperature; 400 | var FTopP := Self.FTopP; 401 | var FN := Self.FN; 402 | var FStop := Self.FStop.ToStringArray; 403 | var FMaxTokens := Self.FMaxTokens; 404 | var FPresencePenalty := Self.FPresencePenalty; 405 | var FFrequencyPenalty := Self.FFrequencyPenalty; 406 | var FUser := Self.FUser; 407 | var FChatTools: TArray := []; 408 | 409 | if Assigned(FFunctions) then 410 | for var Item in FFunctions.GetList do 411 | FChatTools := FChatTools + [TChatToolFunctionParam.Create(Item)]; 412 | 413 | FDoStreamStop := False; 414 | FTask := TaskRun(Self, 415 | procedure(Holder: IComponentHolder) 416 | begin 417 | try 418 | try 419 | var Succ := FClient.Chat.CreateStream( 420 | procedure(Params: TChatParams) 421 | begin 422 | Params.Messages(FMessages); 423 | Params.Model(FModel); 424 | Params.Temperature(FTemperature); 425 | Params.TopP(FTopP); 426 | Params.N(FN); 427 | if Length(FStop) > 0 then 428 | Params.Stop(FStop); 429 | Params.Stream; 430 | Params.MaxTokens(FMaxTokens); 431 | Params.PresencePenalty(FPresencePenalty); 432 | Params.FrequencyPenalty(FFrequencyPenalty); 433 | Params.User(FUser); 434 | if Length(FChatTools) > 0 then 435 | Params.Tools(FChatTools); 436 | end, 437 | procedure(var Chat: TChat; IsDone: Boolean; var Cancel: Boolean) 438 | begin 439 | if FDoStreamStop then 440 | begin 441 | Cancel := True; 442 | Exit; 443 | end; 444 | if not Holder.IsLive then 445 | begin 446 | Cancel := True; 447 | Exit; 448 | end; 449 | if FSync then 450 | begin 451 | var FChat := Chat; 452 | Chat := nil; 453 | Queue( 454 | procedure 455 | begin 456 | try 457 | if Holder.IsLive then 458 | CurrentChat.DoChatDelta(FChat, IsDone); 459 | finally 460 | FChat.Free; 461 | end; 462 | end) 463 | end 464 | else 465 | begin 466 | if Holder.IsLive then 467 | CurrentChat.DoChatDelta(Chat, IsDone); 468 | end; 469 | end); 470 | if not Succ then 471 | raise Exception.Create('Error'); 472 | except 473 | on E: Exception do 474 | begin 475 | if FSync then 476 | begin 477 | var Err := AcquireExceptionObject; 478 | Queue( 479 | procedure 480 | begin 481 | try 482 | if Holder.IsLive then 483 | CurrentChat.DoError(E); 484 | finally 485 | Err.Free; 486 | end; 487 | end); 488 | end 489 | else 490 | begin 491 | if Holder.IsLive then 492 | CurrentChat.DoError(E); 493 | end; 494 | Exit; 495 | end; 496 | end; 497 | finally 498 | if Holder.IsLive then 499 | CurrentChat.DoEndWork; 500 | end; 501 | end); 502 | DoBeginWork; 503 | FTask.Start; 504 | end; 505 | 506 | procedure TOpenAIChatCustom.SetClient(const Value: TOpenAIClient); 507 | begin 508 | FClient := Value; 509 | end; 510 | 511 | procedure TOpenAIChatCustom.SetFrequencyPenalty(const Value: Single); 512 | begin 513 | FFrequencyPenalty := Value; 514 | end; 515 | 516 | procedure TOpenAIChatCustom.SetFunctions(const Value: TOpenAIChatFunctions); 517 | begin 518 | FFunctions := Value; 519 | end; 520 | 521 | procedure TOpenAIChatCustom.SetHistory(const Value: TOpenAIChatHistoryCustom); 522 | begin 523 | FHistory := Value; 524 | end; 525 | 526 | procedure TOpenAIChatCustom.SetMaxTokens(const Value: Integer); 527 | begin 528 | FMaxTokens := Value; 529 | end; 530 | 531 | procedure TOpenAIChatCustom.SetModel(const Value: string); 532 | begin 533 | FModel := Value; 534 | end; 535 | 536 | procedure TOpenAIChatCustom.SetN(const Value: Integer); 537 | begin 538 | FN := Value; 539 | end; 540 | 541 | procedure TOpenAIChatCustom.SetOnBeginWork(const Value: TNotifyEvent); 542 | begin 543 | FOnBeginWork := Value; 544 | end; 545 | 546 | procedure TOpenAIChatCustom.SetOnChat(const Value: TOnChat); 547 | begin 548 | FOnChat := Value; 549 | end; 550 | 551 | procedure TOpenAIChatCustom.SetOnChatDelta(const Value: TOnChatDelta); 552 | begin 553 | FOnChatDelta := Value; 554 | end; 555 | 556 | procedure TOpenAIChatCustom.SetOnEndWork(const Value: TNotifyEvent); 557 | begin 558 | FOnEndWork := Value; 559 | end; 560 | 561 | procedure TOpenAIChatCustom.SetOnError(const Value: TOnError); 562 | begin 563 | FOnError := Value; 564 | end; 565 | 566 | procedure TOpenAIChatCustom.SetOnFunctionCall(const Value: TOnFunctionCall); 567 | begin 568 | FOnFunctionCall := Value; 569 | end; 570 | 571 | procedure TOpenAIChatCustom.SetPresencePenalty(const Value: Single); 572 | begin 573 | FPresencePenalty := Value; 574 | end; 575 | 576 | procedure TOpenAIChatCustom.SetStream(const Value: Boolean); 577 | begin 578 | FStream := Value; 579 | end; 580 | 581 | procedure TOpenAIChatCustom.SetSynchronize(const Value: Boolean); 582 | begin 583 | FSynchronize := Value; 584 | end; 585 | 586 | procedure TOpenAIChatCustom.SetTemperature(const Value: Single); 587 | begin 588 | FTemperature := Value; 589 | end; 590 | 591 | procedure TOpenAIChatCustom.SetTopP(const Value: Single); 592 | begin 593 | FTopP := Value; 594 | end; 595 | 596 | procedure TOpenAIChatCustom.SetUser(const Value: string); 597 | begin 598 | FUser := Value; 599 | end; 600 | 601 | procedure TOpenAIChatCustom.StopStream; 602 | begin 603 | FDoStreamStop := True; 604 | end; 605 | 606 | end. 607 | 608 | -------------------------------------------------------------------------------- /OpenAI.API.pas: -------------------------------------------------------------------------------- 1 | unit OpenAI.API; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.SysUtils, System.Net.HttpClient, System.Net.URLClient, 7 | System.Net.Mime, System.JSON, System.Generics.Collections, OpenAI.Errors, 8 | OpenAI.API.Params; 9 | 10 | type 11 | TOpenAIAPI = class; 12 | 13 | {$IF RTLVersion < 35.0} 14 | TURLClientHelper = class helper for TURLClient 15 | public 16 | const 17 | DefaultConnectionTimeout = 60000; 18 | DefaultSendTimeout = 60000; 19 | DefaultResponseTimeout = 60000; 20 | end; 21 | {$ENDIF} 22 | 23 | OpenAIException = class(Exception) 24 | private 25 | FCode: Int64; 26 | FParam: string; 27 | FType: string; 28 | public 29 | property &Type: string read FType write FType; 30 | property Code: Int64 read FCode write FCode; 31 | property Param: string read FParam write FParam; 32 | constructor Create(const Text, &Type: string; const Param: string = ''; Code: Int64 = -1); reintroduce; 33 | end; 34 | 35 | OpenAIExceptionAPI = class(Exception); 36 | 37 | /// 38 | /// An InvalidRequestError indicates that your request was malformed or 39 | // missing some required parameters, such as a token or an input. 40 | // This could be due to a typo, a formatting error, or a logic error in your code. 41 | /// 42 | OpenAIExceptionInvalidRequestError = class(OpenAIException); 43 | 44 | /// 45 | /// A `RateLimitError` indicates that you have hit your assigned rate limit. 46 | /// This means that you have sent too many tokens or requests in a given period of time, 47 | /// and our services have temporarily blocked you from sending more. 48 | /// 49 | OpenAIExceptionRateLimitError = class(OpenAIException); 50 | 51 | /// 52 | /// An `AuthenticationError` indicates that your API key or token was invalid, 53 | /// expired, or revoked. This could be due to a typo, a formatting error, or a security breach. 54 | /// 55 | OpenAIExceptionAuthenticationError = class(OpenAIException); 56 | 57 | /// 58 | /// This error message indicates that your account is not part of an organization 59 | /// 60 | OpenAIExceptionPermissionError = class(OpenAIException); 61 | 62 | /// 63 | /// This error message indicates that our servers are experiencing high 64 | /// traffic and are unable to process your request at the moment 65 | /// 66 | OpenAIExceptionTryAgain = class(OpenAIException); 67 | 68 | OpenAIExceptionInvalidResponse = class(OpenAIException); 69 | 70 | OpenAIPrepareException = class(OpenAIException); 71 | 72 | IAPIPrepare = interface 73 | ['{10B51102-8D50-40F5-AD6D-E44D9B22A56F}'] 74 | procedure PrepareQuery(API: TOpenAIAPI); 75 | end; 76 | 77 | {$WARNINGS OFF} 78 | TOpenAIAPI = class 79 | public 80 | const 81 | URL_BASE = 'https://api.openai.com/v1'; 82 | private 83 | FToken: string; 84 | FBaseUrl: string; 85 | FOrganization: string; 86 | 87 | FIsAzure: Boolean; 88 | FAzureApiVersion: string; 89 | FAzureDeployment: string; 90 | FCustomHeaders: TNetHeaders; 91 | FProxySettings: TProxySettings; 92 | FConnectionTimeout: Integer; 93 | FSendTimeout: Integer; 94 | FResponseTimeout: Integer; 95 | FAssistantsVersion: string; 96 | FDisableBearerPrefix: Boolean; 97 | FPrepare: IAPIPrepare; 98 | 99 | procedure SetToken(const Value: string); 100 | procedure SetBaseUrl(const Value: string); 101 | procedure SetOrganization(const Value: string); 102 | procedure ParseAndRaiseError(Error: TError; Code: Int64); 103 | procedure ParseError(const Code: Int64; const ResponseText: string); 104 | procedure SetCustomHeaders(const Value: TNetHeaders); 105 | procedure SetProxySettings(const Value: TProxySettings); 106 | procedure SetConnectionTimeout(const Value: Integer); 107 | procedure SetResponseTimeout(const Value: Integer); 108 | procedure SetSendTimeout(const Value: Integer); 109 | protected 110 | function GetHeaders: TNetHeaders; virtual; 111 | function GetRequestURL(const Path: string): string; 112 | function Get(const Path: string; Response: TStream): Integer; overload; 113 | function Delete(const Path: string; Response: TStream): Integer; overload; 114 | function Post(const Path: string; Response: TStream): Integer; overload; 115 | function Post(const Path: string; Body: TJSONObject; Response: TStream; OnReceiveData: TReceiveDataCallback = nil): Integer; overload; 116 | function Post(const Path: string; Body: TMultipartFormData; Response: TStream): Integer; overload; 117 | function ParseResponse(const Code: Int64; const ResponseText: string): T; 118 | procedure CheckAPI; 119 | public 120 | function GetClient: THTTPClient; virtual; 121 | function Get(const Path: string): TResult; overload; 122 | function Get(const Path: string; ParamProc: TProc): TResult; overload; 123 | procedure GetFile(const Path: string; Response: TStream); overload; 124 | function Delete(const Path: string): TResult; overload; 125 | function Post(const Path: string; ParamProc: TProc; Response: TStream; Event: TReceiveDataCallback = nil): Boolean; overload; 126 | function Post(const Path: string; ParamProc: TProc): TResult; overload; 127 | function Post(const Path: string): TResult; overload; 128 | function PostForm(const Path: string; ParamProc: TProc): TResult; overload; 129 | public 130 | constructor Create; overload; 131 | constructor Create(const AToken: string); overload; 132 | destructor Destroy; override; 133 | property Token: string read FToken write SetToken; 134 | property BaseUrl: string read FBaseUrl write SetBaseUrl; 135 | property Organization: string read FOrganization write SetOrganization; 136 | property ProxySettings: TProxySettings read FProxySettings write SetProxySettings; 137 | /// Property to set/get the ConnectionTimeout. Value is in milliseconds. 138 | /// -1 - Infinite timeout. 0 - platform specific timeout. Supported by Windows, Linux, Android platforms. 139 | property ConnectionTimeout: Integer read FConnectionTimeout write SetConnectionTimeout; 140 | /// Property to set/get the SendTimeout. Value is in milliseconds. 141 | /// -1 - Infinite timeout. 0 - platform specific timeout. Supported by Windows, macOS platforms. 142 | property SendTimeout: Integer read FSendTimeout write SetSendTimeout; 143 | /// Property to set/get the ResponseTimeout. Value is in milliseconds. 144 | /// -1 - Infinite timeout. 0 - platform specific timeout. Supported by all platforms. 145 | property ResponseTimeout: Integer read FResponseTimeout write SetResponseTimeout; 146 | property IsAzure: Boolean read FIsAzure write FIsAzure; 147 | property DisableBearerPrefix: Boolean read FDisableBearerPrefix write FDisableBearerPrefix; 148 | property AzureApiVersion: string read FAzureApiVersion write FAzureApiVersion; 149 | property AzureDeployment: string read FAzureDeployment write FAzureDeployment; 150 | property CustomHeaders: TNetHeaders read FCustomHeaders write SetCustomHeaders; 151 | /// 152 | /// Example: v1, v2, ... 153 | /// 154 | property AssistantsVersion: string read FAssistantsVersion write FAssistantsVersion; 155 | property Prepare: IAPIPrepare read FPrepare write FPrepare; 156 | end; 157 | {$WARNINGS ON} 158 | 159 | TOpenAIAPIRoute = class 160 | private 161 | FAPI: TOpenAIAPI; 162 | procedure SetAPI(const Value: TOpenAIAPI); 163 | public 164 | property API: TOpenAIAPI read FAPI write SetAPI; 165 | constructor CreateRoute(AAPI: TOpenAIAPI); reintroduce; 166 | end; 167 | 168 | implementation 169 | 170 | uses 171 | REST.Json, System.NetConsts, OpenAI.Utils.JSON.Cleaner; 172 | 173 | constructor TOpenAIAPI.Create; 174 | begin 175 | inherited; 176 | // Defaults 177 | FConnectionTimeout := TURLClient.DefaultConnectionTimeout; 178 | FSendTimeout := TURLClient.DefaultSendTimeout; 179 | FResponseTimeout := TURLClient.DefaultResponseTimeout; 180 | FToken := ''; 181 | FBaseUrl := URL_BASE; 182 | FIsAzure := False; 183 | FDisableBearerPrefix := False; 184 | FAzureApiVersion := ''; 185 | FAzureDeployment := ''; 186 | end; 187 | 188 | constructor TOpenAIAPI.Create(const AToken: string); 189 | begin 190 | Create; 191 | Token := AToken; 192 | end; 193 | 194 | destructor TOpenAIAPI.Destroy; 195 | begin 196 | inherited; 197 | end; 198 | 199 | function TOpenAIAPI.Post(const Path: string; Body: TJSONObject; Response: TStream; OnReceiveData: TReceiveDataCallback): Integer; 200 | var 201 | Headers: TNetHeaders; 202 | Stream: TStringStream; 203 | Client: THTTPClient; 204 | begin 205 | CheckAPI; 206 | Client := GetClient; 207 | 208 | try 209 | Headers := GetHeaders + [TNetHeader.Create('Content-Type', 'application/json')]; 210 | Stream := TStringStream.Create; 211 | Client.ReceiveDataCallBack := OnReceiveData; 212 | try 213 | Stream.WriteString(Body.ToJSON); 214 | Stream.Position := 0; 215 | Result := Client.Post(GetRequestURL(Path), Stream, Response, Headers).StatusCode; 216 | finally 217 | Client.OnReceiveData := nil; 218 | Stream.Free; 219 | end; 220 | finally 221 | Client.Free; 222 | end; 223 | end; 224 | 225 | function TOpenAIAPI.Get(const Path: string; Response: TStream): Integer; 226 | var 227 | Client: THTTPClient; 228 | begin 229 | CheckAPI; 230 | Client := GetClient; 231 | try 232 | Result := Client.Get(GetRequestURL(Path), Response, GetHeaders).StatusCode; 233 | finally 234 | Client.Free; 235 | end; 236 | end; 237 | 238 | function TOpenAIAPI.Post(const Path: string; Body: TMultipartFormData; Response: TStream): Integer; 239 | var 240 | Client: THTTPClient; 241 | begin 242 | CheckAPI; 243 | Client := GetClient; 244 | try 245 | Result := Client.Post(GetRequestURL(Path), Body, Response, GetHeaders).StatusCode; 246 | finally 247 | Client.Free; 248 | end; 249 | end; 250 | 251 | function TOpenAIAPI.Post(const Path: string; Response: TStream): Integer; 252 | var 253 | Client: THTTPClient; 254 | begin 255 | CheckAPI; 256 | Client := GetClient; 257 | try 258 | Result := Client.Post(GetRequestURL(Path), TStream(nil), Response, GetHeaders).StatusCode; 259 | finally 260 | Client.Free; 261 | end; 262 | end; 263 | 264 | function TOpenAIAPI.Post(const Path: string; ParamProc: TProc): TResult; 265 | var 266 | Response: TStringStream; 267 | Params: TParams; 268 | Code: Integer; 269 | begin 270 | Response := TStringStream.Create('', TEncoding.UTF8); 271 | Params := TParams.Create; 272 | try 273 | if Assigned(ParamProc) then 274 | ParamProc(Params); 275 | Code := Post(Path, Params.JSON, Response); 276 | Result := ParseResponse(Code, Response.DataString); 277 | finally 278 | Params.Free; 279 | Response.Free; 280 | end; 281 | end; 282 | 283 | function TOpenAIAPI.Post(const Path: string; ParamProc: TProc; Response: TStream; Event: TReceiveDataCallback): Boolean; 284 | var 285 | Params: TParams; 286 | Code: Integer; 287 | Strings: TStringStream; 288 | begin 289 | Params := TParams.Create; 290 | try 291 | if Assigned(ParamProc) then 292 | ParamProc(Params); 293 | Code := Post(Path, Params.JSON, Response, Event); 294 | case Code of 295 | 200..299: 296 | Result := True; 297 | else 298 | Result := False; 299 | if Response is TStringStream then 300 | ParseError(Code, TStringStream(Response).DataString) 301 | else 302 | begin 303 | Strings := TStringStream.Create; 304 | try 305 | Response.Position := 0; 306 | Strings.LoadFromStream(Response); 307 | ParseError(Code, Strings.DataString); 308 | finally 309 | Strings.Free; 310 | end; 311 | end; 312 | end; 313 | finally 314 | Params.Free; 315 | end; 316 | end; 317 | 318 | function TOpenAIAPI.Post(const Path: string): TResult; 319 | var 320 | Response: TStringStream; 321 | Code: Integer; 322 | begin 323 | Response := TStringStream.Create('', TEncoding.UTF8); 324 | try 325 | Code := Post(Path, Response); 326 | Result := ParseResponse(Code, Response.DataString); 327 | finally 328 | Response.Free; 329 | end; 330 | end; 331 | 332 | function TOpenAIAPI.Delete(const Path: string; Response: TStream): Integer; 333 | var 334 | Client: THTTPClient; 335 | begin 336 | CheckAPI; 337 | Client := GetClient; 338 | try 339 | Result := Client.Delete(GetRequestURL(Path), Response, GetHeaders).StatusCode; 340 | finally 341 | Client.Free; 342 | end; 343 | end; 344 | 345 | function TOpenAIAPI.Delete(const Path: string): TResult; 346 | var 347 | Response: TStringStream; 348 | Code: Integer; 349 | begin 350 | Response := TStringStream.Create('', TEncoding.UTF8); 351 | try 352 | Code := Delete(Path, Response); 353 | Result := ParseResponse(Code, Response.DataString); 354 | finally 355 | Response.Free; 356 | end; 357 | end; 358 | 359 | function TOpenAIAPI.PostForm(const Path: string; ParamProc: TProc): TResult; 360 | var 361 | Response: TStringStream; 362 | Params: TParams; 363 | Code: Integer; 364 | begin 365 | Response := TStringStream.Create('', TEncoding.UTF8); 366 | Params := TParams.Create; 367 | try 368 | if Assigned(ParamProc) then 369 | ParamProc(Params); 370 | Code := Post(Path, Params, Response); 371 | Result := ParseResponse(Code, Response.DataString); 372 | finally 373 | Params.Free; 374 | Response.Free; 375 | end; 376 | end; 377 | 378 | function TOpenAIAPI.Get(const Path: string; ParamProc: TProc): TResult; 379 | var 380 | Response: TStringStream; 381 | Params: TParams; 382 | Pair: TPair; 383 | Code: Integer; 384 | Pairs: TArray; 385 | QPath: string; 386 | begin 387 | Response := TStringStream.Create('', TEncoding.UTF8); 388 | Params := TParams.Create; 389 | try 390 | if Assigned(ParamProc) then 391 | ParamProc(Params); 392 | Pairs := []; 393 | for Pair in Params.ToStringPairs do 394 | Pairs := Pairs + [Pair.Key + '=' + Pair.Value]; 395 | QPath := Path; 396 | if Length(Pairs) > 0 then 397 | QPath := QPath + '?' + string.Join('&', Pairs); 398 | Code := Get(QPath, Response); 399 | Result := ParseResponse(Code, Response.DataString); 400 | finally 401 | Params.Free; 402 | Response.Free; 403 | end; 404 | end; 405 | 406 | function TOpenAIAPI.Get(const Path: string): TResult; 407 | var 408 | Response: TStringStream; 409 | Code: Integer; 410 | begin 411 | Response := TStringStream.Create('', TEncoding.UTF8); 412 | try 413 | Code := Get(Path, Response); 414 | Result := ParseResponse(Code, Response.DataString); 415 | finally 416 | Response.Free; 417 | end; 418 | end; 419 | 420 | function TOpenAIAPI.GetClient: THTTPClient; 421 | begin 422 | Result := THTTPClient.Create; 423 | Result.ProxySettings := FProxySettings; 424 | Result.ConnectionTimeout := FConnectionTimeout; 425 | Result.ResponseTimeout := FResponseTimeout; 426 | {$IF RTLVersion >= 35.0} 427 | Result.SendTimeout := FSendTimeout; 428 | {$ENDIF} 429 | Result.AcceptCharSet := 'utf-8'; 430 | end; 431 | 432 | procedure TOpenAIAPI.GetFile(const Path: string; Response: TStream); 433 | var 434 | Code: Integer; 435 | Strings: TStringStream; 436 | Client: THTTPClient; 437 | begin 438 | CheckAPI; 439 | Client := GetClient; 440 | try 441 | Code := Client.Get(GetRequestURL(Path), Response, GetHeaders).StatusCode; 442 | case Code of 443 | 200..299: 444 | ; {success} 445 | else 446 | Strings := TStringStream.Create; 447 | try 448 | Response.Position := 0; 449 | Strings.LoadFromStream(Response); 450 | ParseError(Code, Strings.DataString); 451 | finally 452 | Strings.Free; 453 | end; 454 | end; 455 | finally 456 | Client.Free; 457 | end; 458 | end; 459 | 460 | function TOpenAIAPI.GetHeaders: TNetHeaders; 461 | begin 462 | // Additional headers are not required when using azure 463 | if IsAzure then 464 | Exit; 465 | 466 | if DisableBearerPrefix then 467 | Result := [TNetHeader.Create('Authorization', FToken)] + FCustomHeaders 468 | else 469 | Result := [TNetHeader.Create('Authorization', 'Bearer ' + FToken)] + FCustomHeaders; 470 | if not FOrganization.IsEmpty then 471 | Result := Result + [TNetHeader.Create('OpenAI-Organization', FOrganization)]; 472 | if not FAssistantsVersion.IsEmpty then 473 | Result := Result + [TNetHeader.Create('OpenAI-Beta', 'assistants=' + FAssistantsVersion)]; 474 | end; 475 | 476 | function TOpenAIAPI.GetRequestURL(const Path: string): string; 477 | begin 478 | Result := FBaseURL + '/'; 479 | if IsAzure then 480 | Result := Result + AzureDeployment + '/'; 481 | Result := Result + Path; 482 | 483 | // API-Key and API-Version have to be included in the request not header when using azure 484 | if IsAzure then 485 | Result := Result + Format('?api-version=%s&api-key=%s', [AzureApiVersion, Token]); 486 | end; 487 | 488 | procedure TOpenAIAPI.CheckAPI; 489 | begin 490 | if Assigned(FPrepare) then 491 | FPrepare.PrepareQuery(Self); 492 | if FToken.IsEmpty then 493 | raise OpenAIExceptionAPI.Create('Token is empty!'); 494 | if FBaseUrl.IsEmpty then 495 | raise OpenAIExceptionAPI.Create('Base url is empty!'); 496 | end; 497 | 498 | procedure TOpenAIAPI.ParseAndRaiseError(Error: TError; Code: Int64); 499 | begin 500 | case Code of 501 | 429: 502 | raise OpenAIExceptionRateLimitError.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 503 | 400, 404, 415: 504 | raise OpenAIExceptionInvalidRequestError.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 505 | 401: 506 | raise OpenAIExceptionAuthenticationError.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 507 | 403: 508 | raise OpenAIExceptionPermissionError.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 509 | 409: 510 | raise OpenAIExceptionTryAgain.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 511 | else 512 | raise OpenAIException.Create(Error.Message, Error.&Type, Error.Param, Error.Code); 513 | end; 514 | end; 515 | 516 | procedure TOpenAIAPI.ParseError(const Code: Int64; const ResponseText: string); 517 | var 518 | Error: TErrorResponse; 519 | begin 520 | Error := nil; 521 | try 522 | try 523 | Error := TJson.JsonToObject(ResponseText); 524 | except 525 | Error := nil; 526 | end; 527 | if Assigned(Error) and Assigned(Error.Error) then 528 | ParseAndRaiseError(Error.Error, Code) 529 | else 530 | raise OpenAIException.Create('Unknown error. Code: ' + Code.ToString, '', '', Code); 531 | finally 532 | Error.Free; 533 | end; 534 | end; 535 | 536 | function TOpenAIAPI.ParseResponse(const Code: Int64; const ResponseText: string): T; 537 | var 538 | JO: TJSONObject; 539 | {$IF CompilerVersion <= 35} 540 | ClearedJSON: string; // fix for Delphi < 11 versions 541 | {$ENDIF} 542 | begin 543 | Result := nil; 544 | {$IF CompilerVersion <= 35} 545 | ClearedJSON := TJSONCleaner.New.CleanJSON(ResponseText); 546 | {$ENDIF} 547 | case Code of 548 | 200..299: 549 | try 550 | {$IF CompilerVersion <= 35} 551 | Result := TJson.JsonToObject(ClearedJSON); 552 | {$ELSE} 553 | Result := TJson.JsonToObject(ResponseText); 554 | {$ENDIF} 555 | except 556 | try 557 | // try parse as part of object with text field (example, vtt) 558 | {$IF CompilerVersion <= 35} 559 | JO := TJSONObject.Create(TJSONPair.Create('text', ClearedJSON)); 560 | {$ELSE} 561 | JO := TJSONObject.Create(TJSONPair.Create('text', ResponseText)); 562 | {$ENDIF} 563 | try 564 | Result := TJson.JsonToObject(JO); 565 | finally 566 | JO.Free; 567 | end; 568 | except 569 | Result := nil; 570 | end; 571 | end; 572 | else 573 | {$IF CompilerVersion <= 35} 574 | ParseError(Code, ClearedJSON); 575 | {$ELSE} 576 | ParseError(Code, ResponseText); 577 | {$ENDIF} 578 | end; 579 | if not Assigned(Result) then 580 | raise OpenAIExceptionInvalidResponse.Create('Empty or invalid response', '', '', Code); 581 | end; 582 | 583 | procedure TOpenAIAPI.SetBaseUrl(const Value: string); 584 | begin 585 | FBaseUrl := Value; 586 | end; 587 | 588 | procedure TOpenAIAPI.SetConnectionTimeout(const Value: Integer); 589 | begin 590 | FConnectionTimeout := Value; 591 | end; 592 | 593 | procedure TOpenAIAPI.SetCustomHeaders(const Value: TNetHeaders); 594 | begin 595 | FCustomHeaders := Value; 596 | end; 597 | 598 | procedure TOpenAIAPI.SetOrganization(const Value: string); 599 | begin 600 | FOrganization := Value; 601 | end; 602 | 603 | procedure TOpenAIAPI.SetProxySettings(const Value: TProxySettings); 604 | begin 605 | FProxySettings := Value; 606 | end; 607 | 608 | procedure TOpenAIAPI.SetResponseTimeout(const Value: Integer); 609 | begin 610 | FResponseTimeout := Value; 611 | end; 612 | 613 | procedure TOpenAIAPI.SetSendTimeout(const Value: Integer); 614 | begin 615 | FSendTimeout := Value; 616 | end; 617 | 618 | procedure TOpenAIAPI.SetToken(const Value: string); 619 | begin 620 | FToken := Value; 621 | end; 622 | 623 | { OpenAIException } 624 | 625 | constructor OpenAIException.Create(const Text, &Type, Param: string; Code: Int64); 626 | begin 627 | inherited Create(Text); 628 | Self.&Type := &Type; 629 | Self.Code := Code; 630 | Self.Param := Param; 631 | end; 632 | 633 | { TOpenAIAPIRoute } 634 | 635 | constructor TOpenAIAPIRoute.CreateRoute(AAPI: TOpenAIAPI); 636 | begin 637 | inherited Create; 638 | FAPI := AAPI; 639 | end; 640 | 641 | procedure TOpenAIAPIRoute.SetAPI(const Value: TOpenAIAPI); 642 | begin 643 | FAPI := Value; 644 | end; 645 | 646 | end. 647 | 648 | --------------------------------------------------------------------------------