1:- module(prolog2gpt,[
    2   init_gptkey/0,
    3   gpt_models/1, gpt_models/2,
    4   gpt_models_detail/2, 
    5   gpt_extract_data/4,
    6   gpt_extract_fields/3,
    7   gpt_extract_field_pairs/4,
    8   gpt_completions/4, gpt_completions/5,
    9   gpt_edits/4, gpt_edits/5,
   10   gpt_images_create/3, gpt_images_create/4,
   11   gpt_images_edits/4, gpt_images_edits/5,
   12   gpt_images_variations/3, gpt_images_variations/4,
   13   gpt_embeddings/4, gpt_embeddings/5,
   14   gpt_files/1, gpt_files/2,
   15   gpt_files_upload/4, gpt_files_upload/5,
   16   gpt_files_delete/2, gpt_files_delete/3,
   17   gpt_files_retrieve/2, gpt_files_retrieve/3,
   18   gpt_files_retrieve_content/2, gpt_files_retrieve_content/3,
   19   gpt_fine_tunes/1,gpt_fine_tunes/2,gpt_fine_tunes/3, gpt_fine_tunes/4,
   20   gpt_fine_tunes_detail/2, gpt_fine_tunes_detail/3,
   21   gpt_fine_tunes_cancel/2, gpt_fine_tunes_cancel/3,
   22   gpt_fine_tunes_events/2, gpt_fine_tunes_events/3,
   23   gpt_fine_tunes_delete/2, gpt_fine_tunes_delete/3,
   24   gpt_moderations/3, gpt_moderations/4
   25]).

Prolog interface to GPT

Introduction

This module provides prolog predicates to call the GPT API.

Large Language Models (LLM) like GPT essentially predict what text comes next, based on learning the (latent) probabilistic relationships between text tokens. By training the model on massive samples of text, the natural language capabilities have improved dramatically in recent years.

However, such language models can benefit from interaction with other types of modules, such as logic engines. To aid in developing such interactions, this library aims to make it easy to interact with GPT directly from Prolog, using predicates that call the GPT API.

The Prolog predicates are based on the OpenAI API Reference: https://platform.openai.com/docs/api-reference

Usage

  1. Create your GPT account at https://platform.openai.com
  2. Create an API key at https://platform.openai.com/account/api-keys and, as instructed, save that key somewhere (e.g. in a text file in a secure folder).
  3. Set an environment variable called GPTKEY to the key value (don't forget in Linux that if you added the environment variable to your bash startup script, e.g. `~/.bashrc`, then you need to source your `~/.bashrc` script to activate the new environment variable).
  4. Use the prolog2gpt.pro Prolog module as usual @author Richard de Rozario @license MIT */
   56:- use_module(library(http/http_open)).
   57:- use_module(library(http/http_client)).
   58:- use_module(library(http/http_ssl_plugin)).
   59:- use_module(library(http/json)).
   60:- use_module(library(http/http_json)).
   61:- use_module(library(http/json_convert)).
 init_gptkey is semidet
   64%  Get the GPT API Key from the environment variable named `GPTKEY` and create

   65%  a prolog flag (`gptkey`) with the key value. Note: execute this predicate

   66%  before using any of the others, because the gpt key is needed for authorization

   67%  with each gpt api call.

   68%

   69%  Example use:

   70%  ~~~

   71%  :- init_gptkey, current_prolog_flag(gptkey,Key), writeln(Key).

   72%  Key = sk-manycharactersoftheacturalkeyvalue

   73%  ~~~

   74%

   75init_gptkey:-
   76   getenv('GPTKEY',Key),
   77   create_prolog_flag(gptkey,Key,[type(atom)]).
 gpt_models(-Models:list) is semidet
 gpt_models(-Models:json, +Raw:boolean) is semidet
   81%  Get a list of the available GPT models

   82%

   83%  Example use:

   84%  ~~~

   85%  :- gpt_models(Models).

   86%  Models = [babbage,davinci,...]

   87%  ~~~

   88%

   89%  @arg Models    The list of model names, or JSON term with model details

   90%  @arg Raw       If `true` then Models is the raw json result, else Models is a list of model names

   91%

   92gpt_models(Models):- gpt_models(Models,false).
   93gpt_models(Models,Raw):-
   94   current_prolog_flag(gptkey,Key),
   95   http_get('https://api.openai.com/v1/models',Ms,
   96            [authorization(bearer(Key)),application/json]),
   97   (  Raw=false
   98   -> gpt_extract_data(data,id,Ms,Models)
   99   ;  Models=Ms
  100   ).
 gpt_models_detail(+Model:atom, -ModelDetails:json) is semidet
  104%  Get the details of a particular model

  105%

  106%  Example use:

  107%  ~~~

  108%  :- gpt_models('text-davinci-003',Details).

  109%  Details = ...  % the JSON term

  110%  ~~~

  111%

  112%  @arg Model     The model name, Note: put names that end with numeric suffixes in 

  113%                 single quotes, to avoid the numeric being treated as a number. 

  114%                 For example, use `'text-davinci-003'`

  115%  @arg Details   The details of the model as a JSON term

  116%

  117gpt_models_detail(Model,Details):-
  118   current_prolog_flag(gptkey,Key),
  119   atomic_concat('https://api.openai.com/v1/models/',Model,URL),
  120   http_get(URL,Details,[authorization(bearer(Key)),application/json]).
 gpt_extract_data(+Group:atom, +Fielname:atom, +Data:json, -Result:list) is semidet
  124%  Extract a list of field data from a gpt json structure.  Note: this predicate

  125%  makes some simple assumptions about how GPT API result data is structured.

  126%

  127%  Example use:

  128%  ~~~

  129%  :- gpt_models(Ms,true), gpt_extract_data(data,id,Ms,Models).

  130%  Models = ['babbage','text-davinci-001',...]

  131%  ~~~

  132%

  133%  @arg Group     The GPT data group name. e.g. `data`, `choices`,...

  134%  @arg Fieldname The name of the field whose data we want

  135%  @arg Data      The json data list from the GPT API, that contains one or more field values

  136%  @arg Result    The resulting list of data values

  137gpt_extract_data(Group,Fieldname,json(Data),Result):-
  138   member(Group=Fieldlist,Data),
  139   gpt_extract_fields(Fieldname,Fieldlist,Result).
 gpt_extract_fields(+Fieldname:atom, +Data:json, -Result:list) is semidet
  142%  Extract a list of field data from a gpt json structure.  Note: this predicate

  143%  makes some simple assumptions about how GPT API result data is structured.

  144%

  145%  Example use:

  146%  ~~~

  147%  :- Data=[json([id='babbage',object='model']),json([id='text-davinci-001',object='model'])], gpt_extract_data(data,id,Data,Models).

  148%  Models = ['babbage','text-davinci-001']

  149%  ~~~

  150%

  151%  @arg Fieldname The name of the field whose data we want

  152%  @arg Data      The list with json data from the GPT API, that contains one or more field values

  153%  @arg Result    The resulting list of data values

  154gpt_extract_fields(_,[],[]):-!.
  155gpt_extract_fields(Fieldname,[json(Fields)|Fs],Results):-
  156   (  member(Fieldname=R,Fields)
  157   -> Results=[R|Res]
  158   ;  Results=Res
  159   ),
  160   gpt_extract_fields(Fieldname,Fs,Res).
 gpt_extract_field_pairs(+Field1:atom, +Field2:atom, +Data:json, -Result:list) is semidet
  163%  Extract a list of field pairs from a gpt json structure.  Note: this predicate

  164%  makes some simple assumptions about how GPT API result data is structured.

  165%

  166%  Example use:

  167%  ~~~

  168%  :- Data=[json([id='123',filename=file1]),json([id='345',filename=file2])], gpt_extract_field_pairs(filename,id,Data,FieldPairs).

  169%  FieldPairs = [file1-'123',file2-'345']

  170%  ~~~

  171%

  172%  @arg Fieldname The name of the field whose data we want

  173%  @arg Data      The list with json data from the GPT API, that contains one or more field values

  174%  @arg Result    The resulting list of data values

  175gpt_extract_field_pairs(_,_,[],[]):-!.
  176gpt_extract_field_pairs(Field1,Field2,[json(Fields)|Fs],Results):-
  177   (  member(Field1=F1,Fields)
  178      -> (  member(Field2=F2,Fields)
  179         -> Results = [F1-F2|Res]
  180         ;  Results = Res
  181         )
  182      ;  Results = Res
  183   ),!,
  184  gpt_extract_field_pairs(Field1,Field2,Fs,Res).
 gpt_completions(+Model:atom, +Prompt:atom, -Result:text, +Options:list) is semidet
 gpt_completions(+Model:atom, +Prompt:atom, -Result:term, ?Raw:boolean, +Options:list) is semidet
  189%  Get a prompted text completion from a GPT model.

  190%

  191%  Example use:

  192%  ~~~

  193%  :- gpt_completions('text-davinci-003','My favourite animal is ',Result,_,[]),

  194%  Result = ['a dog']

  195%  ~~~

  196%

  197%  @arg Model        The GPT model name, Note: put names that end with numeric suffixes in 

  198%                    single quotes, to avoid the numeric being treated as a number. 

  199%                    For example, use `'text-davinci-003'`

  200%  @arg Prompt       The prompt that GPT will complete

  201%  @arg Result       The text result, or json term with the result from GPT

  202%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  203%                    the Result will be the text completion result

  204%  @arg Options      The model completion options as list of json pair values (see below)

  205%

  206%

  207%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  208%  * suffix=S

  209%    A string (S) that is inserted after the completion

  210%  * max_tokens=M    

  211%    The size of output, where `M` is a natural number (incl. 0).

  212%    GPT-3 can theoretically return up to 4096 tokens, but in practice less than half that. 

  213%    One token is about 4 characters or 0.75 average word length. Defaults to 16.

  214%  * temperature=N     

  215%    Controls "randomness" of output, with `0<=N<=2`. Defaults to 1.

  216%    Higher temperature means text will be more diverse, but

  217%    also risks more grammar mistakes and nonsense. Recommended to

  218%    change either this or `top_p`, but not both.

  219%  * top_p

  220%    An alternative to sampling with `temperature`, called

  221%    nucleus sampling, where the model considers the results

  222%    of the tokens with `top_p` probability mass. So 0.1 means

  223%    only the tokens comprising the top 10% probability mass are

  224%    considered.  Use this, or `temperature`, but not both.

  225%    Defaults to 1.

  226%  * n=N

  227%    The number of completions (e.g. Results) to generate for each

  228%    prompt. Defaults to 1.

  229%  * stream=TF

  230%    If `true` then tokens will be sent as data-only 

  231%    `server-sent events` as they become available, with the

  232%    stream terminated by a `data: [DONE]` message. 

  233%    Defaults to `false`

  234%  * logprobs=N

  235%    Include the log probabilities on most likely tokens. For

  236%    example, if `logprobs=5`, the API will return a list of

  237%    the 5 most likely tokens. The API will always return the

  238%    log probability of the sampled token, so there may be up 

  239%    to `logprobs+1` elements in the response. Defaults to 0.

  240%  * echo=TF

  241%    If `true`, echo back the prompt in addition to 

  242%    the completion. Defaults to `false`

  243%  * stop=S

  244%    (string or list of strings). Up to 4 strings ("sequences")

  245%    where the API will stop generating further tokens. The 

  246%    returned text will not contain the stop sequences. Defaults

  247%    to `null`

  248%  * presence_penalty=N

  249%    Number between -2.0 and 2.0.  Positive values penalize new

  250%    tokens based on whether they appear in the text so far, 

  251%    increase the model's likelihood to talk about new topics.

  252%    Defaults to 0.

  253%  * frequency_penalty=N

  254%    Number between -2.0 and 2.0. Positive values penalize new

  255%    tokens based on their existing frequency in the text so far,

  256%    decreasing the model's likelihood to repeat the same line

  257%    verbatim. Defaults to 0.

  258%  * best_of=N

  259%    Generates best_of completions server-side and returns the "best" 

  260%    (the one with the highest log probability per token). Results cannot be streamed.

  261%

  262%    When used with `n`, `best_of` controls the number of candidate completions 

  263%    and `n` specifies how many to return – `best_of` must be greater than `n`.

  264%

  265%    Note: Because this parameter generates many completions, it can quickly consume 

  266%    your token quota. Use carefully and ensure that you have reasonable settings for 

  267%    `max_tokens` and `stop`.

  268%  * logit_bias=JSON_TERM

  269%    Modify the likelihood of specified tokens appearing in the completion.

  270%

  271%    Accepts a json object that maps tokens (specified by their token ID in the 

  272%    GPT tokenizer) to an associated bias value from -100 to 100. You can use this 

  273%    tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. 

  274%    Mathematically, the bias is added to the logits generated by the model prior to sampling. 

  275%    The exact effect will vary per model, but values between -1 and 1 should decrease or 

  276%    increase likelihood of selection; values like -100 or 100 should result in a ban or 

  277%    exclusive selection of the relevant token.

  278%

  279%    As an example, you can pass `json('50256': -100)` to prevent the `<|endoftext|>` token 

  280%    from being generated.

  281%  * user=S

  282%    A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.

  283%

  284gpt_completions(Model,Prompt,Result,Options):- 
  285   gpt_completions(Model,Prompt,Result,false,Options),!.
  286
  287gpt_completions(Model,Prompt,Result,Raw,Options):-
  288   current_prolog_flag(gptkey,Key),
  289   atom_json_term(D,json([model=Model,prompt=Prompt|Options]),[]),
  290   Data = atom(application/json,D),
  291   http_post('https://api.openai.com/v1/completions',Data,ReturnData,
  292            [authorization(bearer(Key)),application/json]),
  293   (  Raw=false
  294   -> gpt_extract_data(choices,text,ReturnData,Result)
  295   ;  Result= ReturnData
  296   ).
 gpt_edits(+Model:atom, +Instruction:atom, -Result:text, +Options:list) is semidet
 gpt_edits(+Model:atom, +Instruction:atom, -Result:term, ?Raw:boolean, +Options:list) is semidet
  300%  Get a new edit for a given model, input and instruction.  

  301%  Note: Only for the 'text-davinci-edit-001' or 'code-davinci-edit-001' models.

  302%

  303%  Example use:

  304%  ~~~

  305%  :- gpt_edit('text-davinci-001','Fix the spelling mistakes',Result,_,

  306%              [  input='What day of the wek is it?'

  307%              ]),

  308%  Result = 'What day of the week is it?'

  309%  ~~~

  310%

  311%  @arg Model        The GPT model name, Note: put names that end with numeric suffixes in 

  312%                    single quotes, to avoid the numeric being treated as a number. 

  313%                    For example, use `'text-davinci-003'`

  314%  @arg Instruction  The natural language editing instruction.

  315%  @arg Result       The text result, or json term with the result from GPT

  316%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  317%                    the Result will be the (first) text result

  318%  @arg Options      The edit options as list of json pair values (see below)

  319%

  320%

  321%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  322%  * input=S

  323%    An atom of text (S) that the model needs to edit. Default=''

  324%  * max_tokens=M    

  325%    The size of output, where `M` is a natural number (incl. 0).

  326%    GPT-3 can theoretically return up to 4096 tokens, but in practice less than half that. 

  327%    One token is about 4 characters or 0.75 average word length. Defaults to 16.

  328%  * n=N

  329%    The number of completions (e.g. Results) to generate for each

  330%    prompt. Defaults to 1.

  331%  * temperature=N     

  332%    Controls "randomness" of output, with `0<=N<=2`. Defaults to 1.

  333%    Higher temperature means text will be more diverse, but

  334%    also risks more grammar mistakes and nonsense. Recommended to

  335%    change either this or `top_p`, but not both.

  336%  * top_p

  337%    An alternative to sampling with `temperature`, called

  338%    nucleus sampling, where the model considers the results

  339%    of the tokens with `top_p` probability mass. So 0.1 means

  340%    only the tokens comprising the top 10% probability mass are

  341%    considered.  Use this, or `temperature`, but not both.

  342%    Defaults to 1.

  343gpt_edits(Model,Instruction,Result,Options):- 
  344   gpt_edits(Model,Instruction,Result,false,Options),!.
  345
  346gpt_edits(Model,Instruction,Result,Raw,Options):-
  347   current_prolog_flag(gptkey,Key),
  348   atom_json_term(D,json([model=Model,instruction=Instruction|Options]),[]),
  349   Data = atom(application/json,D),
  350   http_post('https://api.openai.com/v1/edits',Data,ReturnData,
  351            [authorization(bearer(Key)),application/json]),
  352   (  Raw=false
  353   -> gpt_extract_data(choices,text,ReturnData,Result)
  354   ;  Result= ReturnData
  355   ).
 gpt_images_create(+Prompt:atom, -Result:term, +Options:list) is semidet
 gpt_images_create(+Prompt:atom, -Result:term, ?Raw:boolean, +Options:list) is semidet
  359%  Create an image from a text prompt.

  360%

  361%  Example use:

  362%  ~~~

  363%  :- gpt_images_create('A cute baby sea otter',Result,_,[]),

  364%  Result = ['https://...'] % url of the resulting image

  365%  ~~~

  366%

  367%  @arg Prompt       The prompt that GPT will complete

  368%  @arg Result       The text result, or json term with the result from GPT

  369%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  370%                    the Result will be the (first) url or b64 result

  371%  @arg Options      The edit options as list of json pair values (see below)

  372%

  373%

  374%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  375%  * n=N

  376%    The number of images to generate. Defaults to 1.

  377%  * size=Z

  378%    The size of the image. Must be one of `'256x256'`, `'512x512'`, or `'1024x1024'`. 

  379%    Default is `'1024x1024'`

  380%  * response_format=S

  381%    The format of the generated images. Must be one of `url` or `b64_json`. Default is `url`

  382%  * user=S

  383%    A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.

  384%

  385gpt_images_create(Prompt,Result,Options):-
  386   gpt_images_create(Prompt,Result,false,Options).
  387gpt_images_create(Prompt,Result,Raw,Options):-
  388   current_prolog_flag(gptkey,Key),
  389   atom_json_term(D,json([prompt=Prompt|Options]),[]),
  390   Data = atom(application/json,D),
  391   http_post('https://api.openai.com/v1/images/generations',Data,ReturnData,
  392            [authorization(bearer(Key)),application/json]),
  393   ( member(response_format=Format,Options) -> true ; Format=url ),
  394   (  Raw=false
  395   -> gpt_extract_data(data,Format,ReturnData,Result)
  396   ;  Result= ReturnData
  397   ).
 gpt_images_edits(+Prompt:atom, -Result:term, +Options:list) is semidet
 gpt_images_edits(+Prompt:atom, -Result:term, ?Raw:boolean, +Options:list) is semidet
  401%  Modify an image from a text prompt.

  402%

  403%  Example use:

  404%  ~~~

  405%  :- gpt_images_edits('A cute baby sea otter with a hat','./test/otter.png',Result,_,[]),

  406%  Result = ['https://...'] % url of the resulting image

  407%  ~~~

  408%

  409%  @arg Prompt       The prompt that GPT will complete

  410%  @arg File         The path/filename of the image to edit

  411%  @arg Result       The text result, or json term with the result from GPT

  412%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  413%                    the Result will be the (first) url or b64 result

  414%  @arg Options      The edit options as list of pair values (see below)

  415%

  416%

  417%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  418%  * n=N

  419%    The number of images to generate. Defaults to 1.

  420%  * size=Z

  421%    The size of the image. Must be one of `'256x256'`, `'512x512'`, or `'1024x1024'`. 

  422%    Default is `'1024x1024'`

  423%  * mask

  424%    An additional image whose fully transparent areas (e.g. where alpha is zero) 

  425%    indicate where image should be edited. Must be a valid 

  426%    PNG file, less than 4MB, and have the same dimensions as image.

  427%  * response_format=S

  428%    The format of the generated images. Must be one of `url` or `b64_json`. Default is `url`

  429%  * user=S

  430%    A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.

  431%

  432gpt_images_edits(Prompt,Image,Result,Options):-
  433   gpt_images_edits(Prompt,Image,Result,false,Options).
  434gpt_images_edits(Prompt,Image,Result,Raw,Options):-
  435   current_prolog_flag(gptkey,Key),
  436   Data = form_data([prompt=Prompt,image=file(Image)|Options]),
  437   http_post('https://api.openai.com/v1/images/edits',Data,ReturnData,
  438            [authorization(bearer(Key)),application/json]),
  439   ( member(response_format=Format,Options) -> true ; Format=url ),
  440   (  Raw=false
  441   -> gpt_extract_data(data,Format,ReturnData,Result)
  442   ;  Result= ReturnData
  443   ).
 gpt_images_variations(+File:atom, -Result:term, +Options:list) is semidet
 gpt_images_variations(+File:atom, -Result:term, ?Raw:boolean, +Options:list) is semidet
  447%  Produce variation(s) of an image.

  448%

  449%  Example use:

  450%  ~~~

  451%  :- gpt_images_variations('./test/otter.png',Result,_,[]),

  452%  Result = ['https://...'] % url of the resulting image

  453%  ~~~

  454%

  455%  @arg Image        The path/filename of image to vary

  456%  @arg Result       The text result, or json term with the result from GPT

  457%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  458%                    the Result will be the (first) url or b64 result

  459%  @arg Options      The edit options as list of pair values (see below)

  460%

  461%

  462%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  463%  * n=N

  464%    The number of images to generate. Defaults to 1.

  465%  * size=Z

  466%    The size of the image. Must be one of `'256x256'`, `'512x512'`, or `'1024x1024'`. 

  467%    Default is `'1024x1024'`

  468%  * response_format=S

  469%    The format of the generated images. Must be one of `url` or `b64_json`. Default is `url`

  470%  * user=S

  471%    A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.

  472%

  473gpt_images_variations(Image,Result,Options):-
  474   gpt_images_variations(Image,Result,false,Options).
  475
  476gpt_images_variations(Image,Result,Raw,Options):-
  477   current_prolog_flag(gptkey,Key),
  478   Data = form_data([image=file(Image)|Options]),
  479   http_post('https://api.openai.com/v1/images/variations',Data,ReturnData,
  480            [authorization(bearer(Key)),application/json]),
  481   ( member(response_format=Format,Options) -> true ; Format=url ),
  482   (  Raw=false
  483   -> gpt_extract_data(data,Format,ReturnData,Result)
  484   ;  Result= ReturnData
  485   ).
 gpt_embeddings(+Input:text, -Result:list) is semidet
 gpt_embeddings(+Input:text, -Result:list, +Raw:boolean) is semidet
  489%  Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.

  490%

  491%  Example use:

  492%  ~~~

  493%  :- gpt_embeddings('text-embedding-ada-002','The food was delicious',Result),

  494%  Result = [0.0023064255,-0.009327292,...]

  495%  ~~~

  496%

  497%  @arg Input        Atom, string, or list of such

  498%  @arg Result       List of file names, or json term (depending on `Raw`)

  499%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  500%                    the Result will be a simple list of file names

  501%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  502%  * user=S

  503%    A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.

  504%

  505gpt_embeddings(Model,Input,Result,Options):-
  506   gpt_embeddings(Model,Input,Result,false,Options),!.
  507gpt_embeddings(Model,Input,Result,Raw,Options):-
  508   current_prolog_flag(gptkey,Key),
  509   atom_json_term(D,json([model=Model,input=Input|Options]),[]),
  510   Data = atom(application/json,D),
  511   http_post('https://api.openai.com/v1/embeddings',Data,ReturnData,
  512            [authorization(bearer(Key)),application/json]),
  513   (  Raw=false
  514   -> gpt_extract_data(data,embedding,ReturnData,Result)
  515   ;  Result= ReturnData
  516   ).
 gpt_files(-Result:list) is semidet
 gpt_files(-Result:list, +Raw:boolean) is semidet
  521%  List all files that belong to the user's organization.

  522%

  523%  Example use:

  524%  ~~~

  525%  :- gpt_files(Result),

  526%  Result = ['file1.jsonl'-'file-12345','file2.jsonl'-'file-56789']

  527%  ~~~

  528%

  529%  @arg Result       List of Filename-ID pairs, or json term (depending on `Raw`)

  530%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  531%                    the Result will be a simple list of file names

  532gpt_files(Result):-
  533   gpt_files(Result,false).
  534gpt_files(Result,Raw):-
  535   current_prolog_flag(gptkey,Key),
  536   http_get('https://api.openai.com/v1/files',json(ReturnData),
  537            [authorization(bearer(Key)),application/json]),
  538   (  Raw=false
  539   -> (  member(data=Files,ReturnData),
  540         gpt_extract_field_pairs(filename,id,Files,Result)
  541      )
  542   ;  Result= json(ReturnData)
  543   ).
 gpt_files_upload(+File:atom, +Purpose:text, -Result:list) is semidet
 gpt_files_upload(+File:atom, +Purpose:text, -Result:list, +Raw:boolean) is semidet
  547%  Upload a JSON Lines file (typically for fine-tuning)

  548%

  549%  Example use:

  550%  ~~~

  551%  :- gpt_files_upload('./test/tune_answer.jsonl','fine-tune',Result),

  552%  Result = ['file-XjGxS3KTG0uNmNOK362iJua3']

  553%  ~~~

  554%

  555%  @arg File         Filename to upload

  556%  @arg Purpose      Purpose of the file. Currently only 'fine-tune'

  557%  @arg Result       List of file names, or json term (depending on `Raw`)

  558%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  559%                    the Result will be a simple list of file names

  560gpt_files_upload(File,Purpose,Result,Options):-
  561   gpt_files_upload(File,Purpose,Result,false,Options),!.
  562gpt_files_upload(File,Purpose,Result,Raw,Options):-
  563   current_prolog_flag(gptkey,Key), 
  564   Data = form_data([file=file(File),purpose=Purpose|Options]),
  565   http_post('https://api.openai.com/v1/files',Data,json(ReturnData),
  566            [authorization(bearer(Key)),application/json]),
  567   (  Raw=false
  568   -> (member(id=ID,ReturnData),Result=[ID])
  569   ;  Result= json(ReturnData)
  570   ).
 gpt_files_delete(+FileID:atom, +Purpose:text, -Result:list) is semidet
 gpt_files_delete(+FileID:atom, +Purpose:text, -Result:list, +Raw:boolean) is semidet
  574%  Delete a (user) file from GPT storage

  575%

  576%  Example use:

  577%  ~~~

  578%  :- gpt_files_delete('file-XjGxS3KTG0uNmNOK362iJua3',Result),

  579%  Result = ['file-XjGxS3KTG0uNmNOK362iJua3']

  580%  ~~~

  581%

  582%  @arg FileID       File ID of file in GPT storage to delete

  583%  @arg Purpose      Purpose of the file. Currently only 'fine-tune'

  584%  @arg Result       List of file names, or json term (depending on `Raw`)

  585%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  586%                    the Result will be a simple list of file names

  587gpt_files_delete(FileID,Result):-
  588   gpt_files_delete(FileID,Result,false),!.
  589gpt_files_delete(FileID,Result,Raw):-
  590   current_prolog_flag(gptkey,Key),
  591   atomic_concat('https://api.openai.com/v1/files/',FileID,URL),
  592   http_delete(URL,json(ReturnData),
  593      [authorization(bearer(Key)),application/json]),
  594   (  Raw=false
  595   -> (member(id=ID,ReturnData), Result=[ID])
  596   ;  Result= json(ReturnData)
  597   ).
 gpt_files_retrieve(+FileID:atom, -Result:list) is semidet
 gpt_files_retrieve(+FileID:atom, -Result:list, +Raw:boolean) is semidet
  601%  Retrieve a (user) file details

  602%

  603%  Example use:

  604%  ~~~

  605%  :- gpt_files_retrieve('file-XjGxS3KTG0uNmNOK362iJua3',Result),

  606%  Result = ['myfile.jsonl']

  607%  ~~~

  608%

  609%  @arg FileID       File ID of file in GPT storage to retrieve

  610%  @arg Result       List with file name, or json term (depending on `Raw`)

  611%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  612%                    the Result will be a simple list of file names

  613gpt_files_retrieve(FileID,Result):-
  614   gpt_files_retrieve(FileID,Result,false),!.
  615gpt_files_retrieve(FileID,Result,Raw):-
  616   current_prolog_flag(gptkey,Key),
  617   atomic_concat('https://api.openai.com/v1/files/',FileID,URL),
  618   http_get(URL,json(ReturnData),
  619      [authorization(bearer(Key)),application/json]),
  620   (  Raw=false
  621   -> (member(filename=File,ReturnData), Result=[File])
  622   ;  Result= json(ReturnData)
  623   ).
 gpt_files_retrieve_content(+FileID:atom, +Purpose:text, -Result:list) is semidet
 gpt_files_retrieve(+FileID:atom, +Purpose:text, -Result:list, +Raw:boolean) is semidet
  627%  Retrieve a (user) file details

  628%

  629%  Example use:

  630%  ~~~

  631%  :- gpt_files_retrieve('file-XjGxS3KTG0uNmNOK362iJua3',Result),

  632%  Result = ['myfile.jsonl']

  633%  ~~~

  634%

  635%  @arg FileID       File ID of file in GPT storage to retrieve

  636%  @arg Result       List with file name, or json term (depending on `Raw`)

  637%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  638%                    the Result will be a simple list of file names

  639% TODO: ***** this API doesn't work for some reason *****

  640gpt_files_retrieve_content(FileID,Result):-
  641   gpt_files_retrieve_content(FileID,Result,false),!.
  642gpt_files_retrieve_content(FileID,Result,Raw):-
  643   current_prolog_flag(gptkey,Key),
  644   atomic_list_concat(['https://api.openai.com/v1/files/',FileID,'/content'],URL),
  645   http_get(URL,ReturnData, [authorization(bearer(Key))]),
  646   (  Raw=false
  647   -> (member(filename=File,ReturnData), Result=[File])
  648   ;  Result= ReturnData
  649   ).
 gpt_fine_tunes(+TrainingFile:text, -Result:list) is semidet
 gpt_fine_tunes(+TrainingFile:text, -Result:list, +Raw:boolean) is semidet
  655%  Get a vector representation of a given TrainingFile that can be easily consumed by machine learning models and algorithms.

  656%

  657%  Example use:

  658%  ~~~

  659%  :- gpt_fine_tunes('file-XGinujblHPwGLSztz8cPS8XY',Result),

  660%  Result = ['ft-AF1WoRqd3aJAHsqc9NY7iL8F']

  661%  ~~~

  662%

  663%  @arg TrainingFile Atom with the GPT file ID of an uploaded file

  664%  @arg Result       Fine-tuned request event in list, or json term of details (depending on `Raw`)

  665%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  666%                    the Result will be a simple list of file names

  667%  Options (Note option descriptions are mostly from the GPT API reference -- see the https://platform.openai.com/docs/api-reference for up-to-date and further details):

  668%  * validation_file=F

  669%    The ID of an uploaded file that contains validation data.

  670%

  671%    If you provide this file, the data is used to generate validation 

  672%    metrics periodically during fine-tuning. These metrics can be viewed 

  673%    in the fine-tuning results file. Your train and validation data should 

  674%    be mutually exclusive.

  675%

  676%    Your dataset must be formatted as a JSONL file, where each validation 

  677%    example is a JSON object with the keys "prompt" and "completion". 

  678%    Additionally, you must upload your file with the purpose fine-tune.

  679%  * model=M

  680%    The name of the base model to fine-tune. You can select one of 'ada', 

  681%    'babbage', 'curie', 'davinci', or a fine-tuned model created after 

  682%    2022-04-21. To learn more about these models, see the Models documentation.

  683%    Defaults to 'curie'.

  684%  * n_epochs=N

  685%    The number of epochs to train the model for. An epoch refers to one full 

  686%    cycle through the training dataset. Defaults to 4.

  687%  * batch_size=N

  688%    The batch size to use for training. The batch size is the number of 

  689%    training examples used to train a single forward and backward pass.

  690%

  691%    By default, the batch size will be dynamically configured to be ~0.2% 

  692%    of the number of examples in the training set, capped at 256 - in 

  693%    general, we've found that larger batch sizes tend to work better for 

  694%    larger datasets. Defaults to `null`.

  695%  * learning_rate_multiplier=N

  696%    The learning rate multiplier to use for training. The fine-tuning 

  697%    learning rate is the original learning rate used for pretraining 

  698%    multiplied by this value.

  699%

  700%    By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 

  701%    depending on final batch_size (larger learning rates tend to perform 

  702%    better with larger batch sizes). We recommend experimenting with 

  703%    values in the range 0.02 to 0.2 to see what produces the best results.

  704%    Defaults to `null`.

  705%  * prompt_loss_weight=N

  706%    The weight to use for loss on the prompt tokens. This controls how 

  707%    much the model tries to learn to generate the prompt (as compared to 

  708%    the completion which always has a weight of 1.0), and can add a 

  709%    stabilizing effect to training when completions are short.

  710%

  711%    If prompts are extremely long (relative to completions), it may make 

  712%    sense to reduce this weight so as to avoid over-prioritizing learning 

  713%    the prompt. Defaults to `0.01`

  714%  * compute_classification_metrics=B

  715%    If set, we calculate classification-specific metrics such as accuracy 

  716%    and F-1 score using the validation set at the end of every epoch. 

  717%    These metrics can be viewed in the results file.

  718%

  719%    In order to compute classification metrics, you must provide a 

  720%    validation_file. Additionally, you must specify classification_n_classes 

  721%    for multiclass classification or classification_positive_class for 

  722%    binary classification. Defaults to `false`

  723%  * classification_n_classes=N

  724%    The number of classes in a classification task. This parameter is 

  725%    required for multiclass classification. Defaults to `null`.

  726%  * classification_positive_class=S

  727%    The positive class in binary classification. This parameter is needed 

  728%    to generate precision, recall, and F1 metrics when doing binary 

  729%    classification. Defaults to `null`.

  730%  * classification_betas=List

  731%    If this is provided, we calculate F-beta scores at the specified beta 

  732%    values. The F-beta score is a generalization of F-1 score. This is only 

  733%    used for binary classification.

  734%

  735%    With a beta of 1 (i.e. the F-1 score), precision and recall are given 

  736%    the same weight. A larger beta score puts more weight on recall and 

  737%    less on precision. A smaller beta score puts more weight on precision 

  738%    and less on recall. Defaults to `null`.

  739%  * suffix=S

  740%    A string of up to 40 characters that will be added to your fine-tuned 

  741%    model name. For example, a suffix of "custom-model-name" would produce 

  742%    a model name like `ada:ft-your-org:custom-model-name-2022-02-15-04-21-04`.

  743%

  744gpt_fine_tunes(TrainingFile,Result,Options):-
  745   gpt_fine_tunes(TrainingFile,Result,false,Options),!.
  746gpt_fine_tunes(TrainingFile,Result,Raw,Options):-
  747   current_prolog_flag(gptkey,Key),
  748   atom_json_term(D,json([training_file=TrainingFile|Options]),[]),
  749   Data = atom(application/json,D),
  750   http_post('https://api.openai.com/v1/fine-tunes',Data,json(ReturnData),
  751            [authorization(bearer(Key)),application/json]),
  752   (  Raw=false
  753   -> member(id=Result,ReturnData)
  754   ;  Result= json(ReturnData)
  755   ).
 gpt_fine_tunes(-Result:list) is semidet
 gpt_fine_tunes(-Result:list, +Raw:boolean) is semidet
  759%  Gets a list of fine-tunes jobs.

  760%

  761%  Example use:

  762%  ~~~

  763%  :- gpt_fine-tunes(Result),

  764%  Result = ['curie:ft-personal-2022-02-15-04-21-04'-'ft-090asf0asf0',...]

  765%  ~~~

  766%

  767%  @arg Result       List with file name, or json term (depending on `Raw`)

  768%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  769%                    the Result will be a simple list of file names

  770gpt_fine_tunes(Result):-
  771   gpt_fine_tunes(Result,false),!.
  772gpt_fine_tunes(Result,Raw):-
  773   current_prolog_flag(gptkey,Key),
  774   http_get('https://api.openai.com/v1/fine-tunes',json(ReturnData),
  775      [authorization(bearer(Key)),application/json]),
  776   (  Raw=false
  777   -> (  member(data=Models,ReturnData),
  778         gpt_extract_field_pairs(fine_tuned_model,id,Models,Result)
  779      )
  780   ;  Result= json(ReturnData)
  781   ).
 gpt_fine_tunes_detail(+ID:atom, -Result:list) is semidet
 gpt_fine_tunes_detail(+ID:atom, -Result:list, +Raw:boolean) is semidet
  785%  Gets details of a fine-tunes job.

  786%

  787%  Example use:

  788%  ~~~

  789%  :- gpt_fine_tunes_detail('ft-090asf0asf0',Result),

  790%  Result = ['curie:ft-personal-2022-02-15-04-21-04']

  791%  ~~~

  792%

  793%  @arg Result       List with file name, or json term (depending on `Raw`)

  794%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  795%                    the Result will be a simple list of file names

  796gpt_fine_tunes_detail(ID,Result):-
  797   gpt_fine_tunes_detail(ID,Result,false),!.
  798gpt_fine_tunes_detail(ID,Result,Raw):-
  799   current_prolog_flag(gptkey,Key),
  800   atomic_concat('https://api.openai.com/v1/fine-tunes/',ID,URL),
  801   http_get(URL,json(ReturnData),
  802      [authorization(bearer(Key)),application/json]),
  803   (  Raw=false
  804   -> (  member(fine_tuned_model=TunedModel,ReturnData),
  805         Result=[TunedModel]
  806      )
  807   ;  Result= json(ReturnData)
  808   ).
 gpt_fine_tunes_cancel(+ID:atom, -Result:list) is semidet
 gpt_fine_tunes_cancel(+ID:atom, -Result:list, +Raw:boolean) is semidet
  812%  Cancel a fine-tunes job.

  813%

  814%  Example use:

  815%  ~~~

  816%  :- gpt_fine_tunes_cancel([_-ID]),(ID,Result),

  817%  Result = ['curie:ft-personal-2022-02-15-04-21-04']

  818%  ~~~

  819%

  820%  @arg ID           ID of the fine-tunes job

  821%  @arg Result       List with file name, or json term (depending on `Raw`)

  822%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  823%                    the Result will be a simple list of file names

  824% TODO: ***** DOES NOT WORK **** something to do with post without data?

  825gpt_fine_tunes_cancel(ID,Result):-
  826   gpt_fine_tunes_cancel(ID,Result,false),!.
  827gpt_fine_tunes_cancel(ID,Result,Raw):-
  828   current_prolog_flag(gptkey,Key),
  829   atomic_list_concat(['https://api.openai.com/v1/fine-tunes/',ID,'/cancel'],URL),
  830   http_post(URL,[],json(ReturnData),
  831      [authorization(bearer(Key)),application/json]),
  832   (  Raw=false
  833   -> (  member(fine_tuned_model=TunedModel,ReturnData),
  834         Result=[TunedModel]
  835      )
  836   ;  Result= json(ReturnData)
  837   ).
 gpt_fine_tunes_events(+ID:atom, -Result:list) is semidet
 gpt_fine_tunes_events(+ID:atom, -Result:list, +Raw:boolean) is semidet
  841%  List events of a fine-tunes job.

  842%

  843%  Example use:

  844%  ~~~

  845%  :- gpt_fine_tunes_events([_-ID]),(ID,Result),

  846%  Result = ['curie:ft-personal-2022-02-15-04-21-04']

  847%  ~~~

  848%

  849%  @arg ID           ID of the fine-tunes job

  850%  @arg Result       List with file name, or json term (depending on `Raw`)

  851%  @arg Raw          If `true` the Result will be the json term, if `false` (default)

  852%                    the Result will be a simple list of file names

  853% TODO: ***** DOES NOT WORK **** something to do with post without data?

  854gpt_fine_tunes_events(ID,Result):-
  855   gpt_fine_tunes_events(ID,Result,false),!.
  856gpt_fine_tunes_events(ID,Result,Raw):-
  857   current_prolog_flag(gptkey,Key),
  858   atomic_list_concat(['https://api.openai.com/v1/fine-tunes/',ID,'/events'],URL),
  859   http_get(URL,json(ReturnData),
  860      [authorization(bearer(Key)),application/json]),
  861   (  Raw=false
  862   -> (  member(fine_tuned_model=TunedModel,ReturnData),
  863         Result=[TunedModel]
  864      )
  865   ;  Result= json(ReturnData)
  866   ).
 gpt_fine_tunes_delete(+ID:atom, -Result:list) is semidet
 gpt_fine_tunes_delete(+ID:atom, -Result:list, +Raw:boolean) is semidet
  870%  Delete a fine-tunes job from GPT storage

  871%

  872%  Example use:

  873%  ~~~

  874%  :- gpt_fine_tunes([_-ID]),gpt_fine_tunes_delete(ID,Result),

  875%  Result = ['ft-XjGxS3KTG0uNmNOK362iJua3']

  876%  ~~~

  877%

  878%  @arg ID       File ID of file in GPT storage to delete

  879%  @arg Purpose  Purpose of the file. Currently only 'fine-tune'

  880%  @arg Result   List of file names, or json term (depending on `Raw`)

  881%  @arg Raw      If `true` the Result will be the json term, if `false` (default)

  882%                the Result will be a simple list of file names

  883gpt_fine_tunes_delete(ID,Result):-
  884   gpt_fine_tunes_delete(ID,Result,false),!.
  885gpt_fine_tunes_delete(ID,Result,Raw):-
  886   current_prolog_flag(gptkey,Key),
  887   atomic_concat('https://api.openai.com/v1/models/',ID,URL),
  888   http_delete(URL,json(ReturnData),
  889      [authorization(bearer(Key)),application/json]),
  890   (  Raw=false
  891   -> (member(id=ID,ReturnData), Result=[ID])
  892   ;  Result= json(ReturnData)
  893   ).
 gpt_moderations(+Model:atom, +Input:text, -Result:list, +Options:list) is semidet
  897%  Given a input text, outputs if the model classifies it as violating OpenAI's content policy.

  898%

  899%  Example use:

  900%  ~~~

  901%  :- gpt_moderations('I want to kill them',Result),

  902%  Result = [sexual=false, hate=false, violence=true, 'self-harm'=false, 

  903%  'sexual/minors'=false, 'hate/threatening'=false, 'violence/graphic'=false].

  904%  ~~~

  905%

  906%  @arg Input        Text to test for content policy violation

  907%  @arg Result       JSON structure with policy scores

  908gpt_moderations(Input,Result,Options):-
  909   gpt_moderations(Input,Result,false,Options).
  910gpt_moderations(Input,Result,Raw,Options):-
  911   current_prolog_flag(gptkey,Key), 
  912   atom_json_term(D,json([input=Input|Options]),[]),
  913   Data = atom(application/json,D),
  914   http_post('https://api.openai.com/v1/moderations',Data,ReturnData,
  915            [authorization(bearer(Key)),application/json]),
  916   (  Raw=false
  917   -> (  gpt_extract_data(results,categories,ReturnData,[json(R)]),
  918         maplist(json_pair_boolean,R,Result)
  919      )
  920   ;  Result= ReturnData
  921   ).
  922
  923json_pair_boolean(Name='@'(Boolean),Name=Boolean):-!.
  924json_pair_boolean(Name=Val,Name=Val):-!