Învățând să creez un pachet R cu o redundanță deliberată 🤣 O notă pentru mine

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

🙈 a făcut un pachet R redundant pentru un simplu apeluri OpenAI, dar adevăratul câștig a fost în sfârșit să învețe cum să construiți un pachet R! 🛠️ este eficient? Absolut nu! A meritat timpul și experiența? Da! O voi face din nou? Da! Se va rupe? Da! 🤣

Motivații

Am folosit o mulțime de LLM în ultima vreme în special OpenAI GPT4OMINI prin intermediul reticulate. Încercând să încărcați reticulate -> Import OpenAI -> Initializează -l -> Introduceți API_KEY etc din nou și din nou ori de câte ori vrem să -l folosim pentru un proiect nou sau un nou script, care este foarte redundant, obositor și așa mai departe.

Recent, cu motivație de la
Alec Wong Vă recomandăm cu drag să citesc pachetul R după ce i -am pus o întrebare noob „Cum face un test și să facă modificări ale unui pachet R existent și al unei cereri de tragere?” 🤣 Cartea este minunată! Îmi place foarte mult prima parte, de fapt te învață cum să creezi un pachet R chiar de la început! Înainte de a se scufunda în detalii. Am constatat că este extrem de răcoritor, deoarece puteți crea cu ușurință un MVP chiar în avans pentru uz personal urmând un flux de lucru! 🙌

Având în vedere LLM și dorind să învățăm cum să creăm un pachet R (ahem corect, cele mai bune practici 🫡), de ce nu încercăm să facem propriul nostru pachet numit myopenai !? Bine, există deja un pachet genial numit ellmeriar proiectul meu actual este cel mai redundant (de unde și titlul), dar ce modalitate mai bună de a începe să înveți? Și o vom face în mod ineficient, prin intermediul reticulate (Acest lucru este atunci când vedeți experți R scutură din cap de neîncredere 🤭) în loc de API -ul HTTP! 🤣 Alăturați -mă pentru a învăța cum să creați un pachet R!

Cu toate acestea, în mod serios, acesta nu este un mod eficient de a utiliza API -ul OpenAI … este doar în scop demonstrativ.

Codul original

reticulate::use_virtualenv("openai")
library(reticulate)

openai <- import("openai")
OpenAI <- openai$OpenAI

client = OpenAI(api_key = 'YOUR API KEY') ## change this to yours  

response <- client$chat$completions$create(
  model = "gpt-4o-mini",
  messages = list(dict(
    role = "system",
    content = "You are a summarizer. Please summarize the following abstract. 
    Include statistics.Use emoji to shorten characters. The summary must not 
    exceed 200 characters—if you reach 200 characters, stop immediately. 
    Do not add any extra commentary or exceed the 200-character limit."
  ), dict(
    role = "user",
    content = df_new(10,"item_description") |> pull()
  )
  ),
  temperature = 0
)

(summary <- response$choices((1))$message$content)

Cele de mai sus au fost codul nostru anterior. Acum vom folosi codul de mai sus, îl vom transforma într -o funcție și sperăm că la sfârșitul proiectului vom avea o bibliotecă numită myopenai Și putem numi o funcție chat() și trebuie doar să introduceți promptul sau să țevi promptul în funcție.

Obiective:

Fluxul de lucru

O notă rapidă pentru mine:

  1. Încărcați devTool, create_package ()
  2. use_git (), probabil va avea nevoie de repornire
  3. Încărcați DevTools din nou, scrieți prima funcție
  4. util_r („funcție”), apoi introduceți funcția acolo
  5. Load_all ()
  6. verifica()
  7. Editați descrierea
  8. util_mit_license ()
  9. Introduceți scheletul roxygen2 pe funcție, editați
  10. document()
  11. Din moment ce avem nevoie de alte pachete, use_package ()
  12. verifica()

usethis::create_package

usethis::create_package("myopenai")

S -ar putea să vedeți așa ceva dacă aveți deja un .git În directorul de lucru, încercați să creați un pachet într -un nou director care nu are un director părinte cu .git în ea și se va deschide la o nouă sesiune de proiect cu BASIC, ceva de genul usethiss::create_package("/Users/you/Document/myopenai"). Veți observa că aveți fișiere de bază, cum ar fi.

imagineimagine

Citiți aici pentru ce reprezintă directoarele sau fișierele.

use_git

library(devtools)

use_git()

Veți primi 2 întrebări, practic dacă doriți să utilizați GIT pentru controlul versiunilor și, de asemenea, dacă doriți să vă angajați. Spune da amândoi. Atunci sesiunea de proiect va reporni. Rețineți că de fiecare dată când repornește, trebuie să reîncărcați devtools

Scrieți o funcție

Nu ne -am dat seama care este cea mai bună modalitate de a face acest lucru încă, scriem un cod funcțional minim în altă parte și apoi îl copiem în proiect sau doar creăm un script R Untitlted și scriem un cod funcțional minim înainte use_r() funcţie? Oricum, deocamdată creați un script fără titlu și începeți să scrieți înainte să -l lipiți într -un fișier R mai organizat.

chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L) {
  
  ## If virtualenv does not exist, create and install openai
  if (reticulate::virtualenv_exists(envname = "openai")==FALSE) {
    reticulate::virtualenv_create(envname = "openai", packages = c("openai"))
    }
  
  ## Start Env
  reticulate::use_virtualenv("openai")
  
  ## Initialize OpenAI
  OpenAI = reticulate::import("openai")$OpenAI
  client = OpenAI(api_key = "YOUR API KEY")

  ## Prompt
  response = client$chat$completions$create(
    model = "gpt-4o-mini",
    messages = list(reticulate::dict(
      role = "system",
      content = system
    ),
    reticulate::dict(
      "role" = "user",
      "content" = prompt
    )),
    temperature = temp,
    max_tokens = max_tokens
  )
  
  ## Response
  message = response$choices((1))$message$content

  return(message)
}

Bine, codul de mai sus este destul de asemănător cu originalul nostru, dar cel puțin acum este o funcție și avem inserați niște parametri impliciti, astfel încât atunci când apelăm chat() Fără parametri în el, cel puțin va returna ceva. Nota de mai sus nu este deloc eficientă, cel mai bine să comunici cu OpenAI API este încă prin intermediul httr2veți avea mult mai puține dependențe. De asemenea, rețineți că mai sus este din nou, nu pentru cea mai mare practică de codificare defensivă, dar putem îmbunătăți de acolo, inclusiv setarea cheii API în .Rprofile.

usethis::use_r()

usethis::use_r("chat")

Acest lucru va crea un fișier R necompletat numit chat.R în folderul R. Apoi copiem codul nostru de funcții minime în acest script. Nu rulați încă nimic! Acesta este următorul pas. Nu trebuie neapărat să creăm un script R pentru fiecare funcție. Ar trebui să-l numim cel puțin acolo unde este Machine & Human Readable, Sortat-Friendly, dacă data folosită Citește aici mai multe despre Ghidul de stil Tidy.

Despre organizare? Este greu, ca brevetul inteligent

Este greu să descrieți exact cum ar trebui să vă organizați codul pe mai multe fișiere. Cred că cea mai bună regulă este că, dacă puteți da unui fișier un nume concis care își evocă încă conținutul, ați ajuns la o organizație bună. Dar a ajunge la acest punct este greu. -Ghid de stil Tidyverse

devtools::load_all()

Pe consolă, introduceți următoarele:

devtools::load_all()

Acest lucru va face întregul pachet disponibil / interactiv. Și atunci ne putem testa funcția și depunerea dacă există o eroare. După cum puteți vedea în imaginea de mai sus, când apelăm chat() a returnat un răspuns. Cartea explică diferența dintre source şi running as a libraryAici

devtools::check()

devtools::check()

Funcția check() este esențial, deoarece efectuează teste cuprinzătoare pentru a asigura că toate componentele unui pachet R funcționează corect, prinzând probleme mai devreme atunci când sunt mai ușor de remediat. Validează faptul că pachetul dvs. respectă standardele CRAN, menținând calitatea codului și prevenind problemele din aval pentru utilizatori. Verificarea periodică stabilește obiceiuri bune de dezvoltare, în special înainte de a -ți împărtăși pachetul cu alții sau de a -l trimite către depozite.

Imaginea de mai sus a arătat că avem 2 avertismente, să o remediem!

Editați descrierea

Înainte de a începe să remediem cele 2 lucruri de mai sus, să edităm descrierea noastră.

Simțiți -vă liber să editați titlul, descrierea, informațiile despre autor

usethis::use_mit_license()

# if you already loaded `devtools` you can just
use_mit_license()

imagineimagine

Introduceți scheletul roxygen2 pe funcție

Faceți clic pe zona funcției de chat, apoi accesați Code > Insert Roxygen2 Skeleton

imagineimagine

Atunci va arăta ceva de genul

Atunci îl puteți edita ca atare

#' GPT4omini chat convenience
#'
#' @param system System Prompt
#' @param prompt User Prompt
#' @param temp Temperature
#' @param max_tokens Max Tokens
#'
#' @return String Response
#' @export
#'
#' @examples
#' chat("what is 2+2?")
chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L) {

  ## If virtualenv does not exist, create and install openai
  if (reticulate::virtualenv_exists(envname = "openai")==FALSE) {
    reticulate::virtualenv_create(envname = "openai", packages = c("openai"))
  }

devtools::document()

imagineimagine

document() Funcția convertește comentariile Roxygen2 în fișierele sursă R în fișiere de documentare R adecvate, creând în mod specific .Rd Fișiere necesare pentru paginile de ajutor și actualizarea fișierului spațiului de nume. Acest proces transformă comentariile speciale care pot fi citite de oameni în structura formală de documentare cerută de R, ceea ce face ca funcțiile pachetului dvs. să fie descoperit și utilizabile prin intermediul sistemului de ajutor standard.

Și când faci asta

?chat

Vei vedea asta

imagineimagine

usethis::use_package()

usethis::use_package("reticulate")

use_package() funcția declară formal dependențe adăugând pachete la Imports Câmp din fișierul de descriere al pachetului dvs., permițându -vă să utilizați funcții din pachete externe precum reticulate.

Să alergăm check() Din nou și install()

devtools::check()

Da. Bun semn !!!

devtools::install()

Și acum puteți merge la RStudio și codul de mai jos ar trebui să funcționeze!

library(myopenai)

"what is 10 + 5?" |> chat()

## (1) "10 + 5 equals 15."

🙌🤘🤩 Am făcut -o !!!

Îmbunătățirea suplimentară a pachetului

Împărțiți fișierele la Chat.R şi utils.R

Chat.r

#' Set you Open API key
#'
#' @param key your API key
#'
#' @return set environment
#' @export
#'
#' @examples
#' dontrun{
#' set_api_key("your-api-key-here")
#' }
set_api_key <- function(key=NULL) {
  if (is.null(key)) { cli::cli_abort("Need to insert API key on key parameter")}
  if (!is.null(key)) {
    key_old <- Sys.getenv("OPENAI_KEY")
    if (key==key_old) { cli::cli_alert_warning("Your previous API key is the same as the one currently provided") }

    if (key!=key_old) {
      set_env(key)
      cli::cli_alert_success("New API Key Inserted")
      rstudioapi::restartSession()
    }

    if (key_old=="") {
      set_env(key)
      cli::cli_alert_success("API Key Inserted")
      rstudioapi::restartSession()
    }
  }
}


#' GPT4omini chat convenience
#'
#' @param system System Prompt
#' @param prompt User Prompt
#' @param temp Temperature
#' @param max_tokens Max Tokens
#' @param api_key use `set_api_key` to set it in R environment
#'
#' @return String Response
#' @export
#'
#' @examples
#' chat("what is 2+2?")
chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L, api_key = openai_api_key()) {

  # check to see if virtualenv is installed
  if (reticulate::virtualenv_exists(envname = "openai")==FALSE) {
    reticulate::virtualenv_create(envname = "openai", packages = c("openai"))
    rstudioapi::restartSession()
  }

  # check to see if there is api_key set
  if (api_key=="") { cli::cli_abort("No API key found. Need to insert API key by using {.code set_api_key()}") }

  ## Start Env
  reticulate::use_virtualenv("openai")

  ## Initialize OpenAI
  OpenAI = reticulate::import("openai")$OpenAI
  client = OpenAI(api_key = api_key)

  ## Prompt
  response = client$chat$completions$create(
    model = "gpt-4o-mini",
    messages = list(reticulate::dict(
      role = "system",
      content = system
    ),
    reticulate::dict(
      "role" = "user",
      "content" = prompt
    )),
    temperature = temp,
    max_tokens = max_tokens
  )

  ## Response
  message = response$choices((1))$message$content

  return(message)
}

utils.r

openai_api_key <- function() {
  key <- Sys.getenv("OPENAI_KEY")

  if (key=="") { cli::cli_abort("No API key found. Need to insert API key by using {.code set_api_key()}") }
  else {
    return(key)
  }
}

set_env <- function(key) {
  renviron_path <- file.path(Sys.getenv("HOME"), ".Renviron")

  api_key_entry <- paste0("OPENAI_KEY=",key)

  if (!file.exists(renviron_path)) {
    file.create(renviron_path)
  }
  
  # Read existing content
  existing_content <- readLines(renviron_path, warn = FALSE)

  if (!any(grepl("^OPENAI_KEY=", existing_content))) {
    # Append the new entry
    write(api_key_entry, renviron_path, append = TRUE)
  } else {
    # Replace existing entry
    existing_content(grepl("^OPENAI_KEY=", existing_content)) <- api_key_entry
    writeLines(existing_content, renviron_path)
  }
}

Citiți mai multe din carte pentru a obține o intuiție de ce avem chat.R şi utils.R. Practic, doriți funcții care sunt exportate pentru a fi în chat.R sau fișier R similar și funcții de susținere care nu sunt exportate pentru a fi în utils.Rîn loc de toate funcțiile din 1 fișier, în principal pentru lizibilitate / organizare. Aceasta este o idee foarte bună.

Există multe alte lucruri pe care să le îmbunătățești. Putem petrece multe ore revizuind acest lucru! Să trecem la testing

Adăugați testarea unității

use_test("chat")

Acest lucru va crea un alt folder (tests) și fișier (test-chat.R)

Test-chat.R

test_that("chat("2+2?") make sure it returns 4", {
  expect_equal(chat("what is 2+2? return answer only") |> as.numeric(), 4)
})

Apoi introducem ceva de genul de mai sus pentru a ne asigura că returnează răspunsul pe care vrem să -l asigurăm că funcționează. Și va arăta așa ceva după.

test()

imagineimagine

🙌🤘🤩 Am făcut -o !!!

Aceasta este o imagine excelentă a fluxului de lucru propriu -zis din carte.

imagineimagine

Confirmare

Sunt recunoscător pentru carte și, de asemenea, stilul de a pune exemplul simplu chiar în față pentru a stimula curiozitatea și a avea sfârșitul în minte. Acest lucru face ca învățarea să fie foarte inspirată. De asemenea, vreau să mulțumesc
Alec Wong Pentru că m -a ghidat către această carte care m -a împins să învăț fluxul de lucru al creării unui pachet R. Acum am o idee despre cum funcționează acest lucru, face ca contribuția la pachetele R existente să fie un pas mai aproape !!! woo hoo !!! Nu că am multe de contribuit 🤣

Lecții învățate

  • Flux de lucru al pachetului R învățat/cele mai bune practici (recomandăm cu mare drag o citire)
  • Învăţat Sys.getenvși cum să -l scrii în mediul R
  • Am învățat câteva elemente de bază ale cli pachet
  • Învăţat dontrun{} pe exemplul Roxygen2, astfel încât să nu execute funcția. Înainte de asta set_api_key() Exemplu a continuat să seteze OpenAI_KEY la your-api-key-here laugh out loud
  • Învăţat grepla fost întotdeauna obișnuit cu str_detect Acum cel puțin putem folosi regex cu bază

Dacă vă place acest articol:

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.