Utilisez l'API Openai avec Ruby! ? ❤️
Diffusez du texte avec GPT-4O, transcrivez et traduisez l'audio avec un chuchotement, ou créez des images avec Dall · E ...
Embauchez-moi pour construire votre application Rails + AI | Rails ai | ? Ruby Ai Builders Discord | ? X | ? Gemme anthropique | ? Gemme midjourney
Ajoutez cette ligne à Gemfile de votre application:
gem "ruby-openai"
Puis exécuter:
$ bundle install
Ou installer avec:
$ gem install ruby-openai
et nécessite avec:
require "openai"
Pour un test rapide, vous pouvez passer votre jeton directement à un nouveau client:
client = OpenAI :: Client . new (
access_token : "access_token_goes_here" ,
log_errors : true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production because it could leak private data to your logs.
)
Pour une configuration plus robuste, vous pouvez configurer la gemme avec vos touches API, par exemple dans un fichier d'initialiseur openai.rb
. Never Hardcode Secrets dans votre base de code - utilisez plutôt quelque chose comme Dotenv pour passer les clés en toute sécurité dans votre environnement.
OpenAI . configure do | config |
config . access_token = ENV . fetch ( "OPENAI_ACCESS_TOKEN" )
config . organization_id = ENV . fetch ( "OPENAI_ORGANIZATION_ID" ) # Optional
config . log_errors = true # Highly recommended in development, so you can see what errors OpenAI is returning. Not recommended in production because it could leak private data to your logs.
end
Ensuite, vous pouvez créer un client comme ceci:
client = OpenAI :: Client . new
Vous pouvez toujours remplacer les défauts de configuration lors de la création de nouveaux clients; Toutes les options non incluses retomberont à un ensemble de configuration global avec openai.configure. par exemple dans cet exemple, l'organisation_id, request_timeout, etc. se repliera à tout ensemble mondial à l'aide d'Openai.configure, avec seulement l'accès_token remplacé:
client = OpenAI :: Client . new ( access_token : "access_token_goes_here" )
request_timeout
lors de l'initialisation du client. client = OpenAI :: Client . new (
access_token : "access_token_goes_here" ,
uri_base : "https://oai.hconeai.com/" ,
request_timeout : 240 ,
extra_headers : {
"X-Proxy-TTL" => "43200" , # For https://github.com/6/openai-caching-proxy-worker#specifying-a-cache-ttl
"X-Proxy-Refresh" : "true" , # For https://github.com/6/openai-caching-proxy-worker#refreshing-the-cache
"Helicone-Auth" : "Bearer HELICONE_API_KEY" , # For https://docs.helicone.ai/getting-started/integration-method/openai-proxy
"helicone-stream-force-format" => "true" , # Use this with Helicone otherwise streaming drops chunks # https://github.com/alexrudall/ruby-openai/issues/251
}
)
ou lors de la configuration du gemme:
OpenAI . configure do | config |
config . access_token = ENV . fetch ( "OPENAI_ACCESS_TOKEN" )
config . log_errors = true # Optional
config . organization_id = ENV . fetch ( "OPENAI_ORGANIZATION_ID" ) # Optional
config . uri_base = "https://oai.hconeai.com/" # Optional
config . request_timeout = 240 # Optional
config . extra_headers = {
"X-Proxy-TTL" => "43200" , # For https://github.com/6/openai-caching-proxy-worker#specifying-a-cache-ttl
"X-Proxy-Refresh" : "true" , # For https://github.com/6/openai-caching-proxy-worker#refreshing-the-cache
"Helicone-Auth" : "Bearer HELICONE_API_KEY" # For https://docs.helicone.ai/getting-started/integration-method/openai-proxy
} # Optional
end
Vous pouvez transmettre dynamiquement des en-têtes par objet client, qui sera fusionné avec tous les en-têtes définis globalement avec openai.configure:
client = OpenAI :: Client . new ( access_token : "access_token_goes_here" )
client . add_headers ( "X-Proxy-TTL" => "43200" )
Par défaut, ruby-openai
ne enregistre aucune Faraday::Error
lors de l'exécution d'une demande de réseau pour éviter de divulguer des données (par exemple 400, 500, des erreurs SSL et plus - voir ici pour une liste complète des sous-classes de Faraday::Error
et des erreurs Qu'est-ce qui peut les provoquer).
Si vous souhaitez activer cette fonctionnalité, vous pouvez définir log_errors
sur true
lors de la configuration du client:
client = OpenAI :: Client . new ( log_errors : true )
Vous pouvez transmettre le middleware Faraday au client dans un bloc, par exemple. Pour permettre la journalisation verbeuse avec l'enregistreur de Ruby:
client = OpenAI :: Client . new do | f |
f . response :logger , Logger . new ( $stdout ) , bodies : true
end
Pour utiliser l'API Azure Openai Service, vous pouvez configurer la gemme comme ceci:
OpenAI . configure do | config |
config . access_token = ENV . fetch ( "AZURE_OPENAI_API_KEY" )
config . uri_base = ENV . fetch ( "AZURE_OPENAI_URI" )
config . api_type = :azure
config . api_version = "2023-03-15-preview"
end
où AZURE_OPENAI_URI
est par exemple https://custom-domain.openai.azure.com/openai/deployments/gpt-35-turbo
Olllama vous permet d'exécuter localement des LLMS open-source, comme Llama 3. Il offre une compatibilité de chat avec l'API OpenAI.
Vous pouvez télécharger Olllama ici. Sur macOS, vous pouvez installer et exécuter Olllama comme ceci:
brew install ollama
ollama serve
ollama pull llama3:latest # In new terminal tab.
Créez un client à l'aide de votre serveur Ollama et du modèle tiré et diffusez une conversation gratuitement:
client = OpenAI :: Client . new (
uri_base : "http://localhost:11434"
)
client . chat (
parameters : {
model : "llama3" , # Required.
messages : [ { role : "user" , content : "Hello!" } ] , # Required.
temperature : 0.7 ,
stream : proc do | chunk , _bytesize |
print chunk . dig ( "choices" , 0 , "delta" , "content" )
end
}
)
# => Hi! It's nice to meet you. Is there something I can help you with, or would you like to chat?
Le chat API GROQ est largement compatible avec l'API OpenAI, avec quelques différences mineures. Obtenez un jeton d'accès à partir d'ici, alors:
client = OpenAI :: Client . new (
access_token : "groq_access_token_goes_here" ,
uri_base : "https://api.groq.com/openai"
)
client . chat (
parameters : {
model : "llama3-8b-8192" , # Required.
messages : [ { role : "user" , content : "Hello!" } ] , # Required.
temperature : 0.7 ,
stream : proc do | chunk , _bytesize |
print chunk . dig ( "choices" , 0 , "delta" , "content" )
end
}
)
Openai Parses invite du texte dans des jetons, qui sont des mots ou des parties de mots. (Ces jetons ne sont pas liés à votre API Access_token.) Le comptage des jetons peut vous aider à estimer vos coûts. Il peut également vous aider à vous assurer que la taille de votre texte invite se situe dans les limites maximales de la fenêtre de contexte de votre modèle et choisissez un paramètre d'achèvement max_tokens
approprié afin que votre réponse s'adapte également.
Pour estimer le nombre de jeton de votre texte:
OpenAI . rough_token_count ( "Your text" )
Si vous avez besoin d'un décompte plus précis, essayez tiktoken_ruby.
Il existe différents modèles qui peuvent être utilisés pour générer du texte. Pour une liste complète et pour récupérer des informations sur un seul modèle:
client . models . list
client . models . retrieve ( id : "gpt-4o" )
GPT est un modèle qui peut être utilisé pour générer du texte dans un style conversationnel. Vous pouvez l'utiliser pour générer une réponse à une séquence de messages:
response = client . chat (
parameters : {
model : "gpt-4o" , # Required.
messages : [ { role : "user" , content : "Hello!" } ] , # Required.
temperature : 0.7 ,
}
)
puts response . dig ( "choices" , 0 , "message" , "content" )
# => "Hello! How may I assist you today?"
Guide rapide pour discuter avec Rails 7 et Hotwire
Vous pouvez diffuser à partir de l'API en temps réel, qui peut être beaucoup plus rapide et utilisé pour créer une expérience utilisateur plus engageante. Passez un proc (ou tout objet avec une méthode #call
) au paramètre stream
pour recevoir le flux de morceaux d'achèvement lorsqu'ils sont générés. Chaque fois qu'un ou plusieurs morceaux sont reçus, le proc sera appelé une fois avec chaque morceau, analysé comme un hachage. Si OpenAI renvoie une erreur, ruby-openai
augmentera une erreur de Faraday.
client . chat (
parameters : {
model : "gpt-4o" , # Required.
messages : [ { role : "user" , content : "Describe a character called Anna!" } ] , # Required.
temperature : 0.7 ,
stream : proc do | chunk , _bytesize |
print chunk . dig ( "choices" , 0 , "delta" , "content" )
end
}
)
# => "Anna is a young woman in her mid-twenties, with wavy chestnut hair that falls to her shoulders..."
Remarque: Afin d'obtenir des informations d'utilisation, vous pouvez fournir le paramètre stream_options
et OpenAI fournira un morceau final avec l'utilisation. Voici un exemple:
stream_proc = proc { | chunk , _bytesize | puts "--------------" ; puts chunk . inspect ; }
client . chat (
parameters : {
model : "gpt-4o" ,
stream : stream_proc ,
stream_options : { include_usage : true } ,
messages : [ { role : "user" , content : "Hello!" } ] ,
}
)
# => --------------
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"role"=>"assistant", "content"=>""}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
# => --------------
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{"content"=>"Hello"}, "logprobs"=>nil, "finish_reason"=>nil}], "usage"=>nil}
# => --------------
# => ... more content chunks
# => --------------
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[{"index"=>0, "delta"=>{}, "logprobs"=>nil, "finish_reason"=>"stop"}], "usage"=>nil}
# => --------------
# => {"id"=>"chatcmpl-7bbq05PiZqlHxjV1j7OHnKKDURKaf", "object"=>"chat.completion.chunk", "created"=>1718750612, "model"=>"gpt-4o-2024-05-13", "system_fingerprint"=>"fp_9cb5d38cf7", "choices"=>[], "usage"=>{"prompt_tokens"=>9, "completion_tokens"=>9, "total_tokens"=>18}}
Vous pouvez utiliser le modèle de vision GPT-4 pour générer une description d'une image:
messages = [
{ "type" : "text" , "text" : "What’s in this image?" } ,
{ "type" : "image_url" ,
"image_url" : {
"url" : "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" ,
} ,
}
]
response = client . chat (
parameters : {
model : "gpt-4-vision-preview" , # Required.
messages : [ { role : "user" , content : messages } ] , # Required.
}
)
puts response . dig ( "choices" , 0 , "message" , "content" )
# => "The image depicts a serene natural landscape featuring a long wooden boardwalk extending straight ahead"
Vous pouvez définir le Response_Format pour demander des réponses dans JSON:
response = client . chat (
parameters : {
model : "gpt-4o" ,
response_format : { type : "json_object" } ,
messages : [ { role : "user" , content : "Hello! Give me some JSON please." } ] ,
temperature : 0.7 ,
} )
puts response . dig ( "choices" , 0 , "message" , "content" )
# =>
# {
# "name": "John",
# "age": 30,
# "city": "New York",
# "hobbies": ["reading", "traveling", "hiking"],
# "isStudent": false
# }
Vous pouvez également le diffuser!
response = client . chat (
parameters : {
model : "gpt-4o" ,
messages : [ { role : "user" , content : "Can I have some JSON please?" } ] ,
response_format : { type : "json_object" } ,
stream : proc do | chunk , _bytesize |
print chunk . dig ( "choices" , 0 , "delta" , "content" )
end
}
)
# =>
# {
# "message": "Sure, please let me know what specific JSON data you are looking for.",
# "JSON_data": {
# "example_1": {
# "key_1": "value_1",
# "key_2": "value_2",
# "key_3": "value_3"
# },
# "example_2": {
# "key_4": "value_4",
# "key_5": "value_5",
# "key_6": "value_6"
# }
# }
# }
Vous pouvez décrire et transmettre des fonctions et le modèle choisira intelligemment de sortir un objet JSON contenant des arguments pour les appeler - par exemple, pour utiliser votre méthode get_current_weather
pour obtenir la météo dans un endroit donné. Notez que TOLL_CHOICE est facultatif, mais si vous l'excluez, le modèle choisira d'utiliser la fonction ou non (voir ici).
def get_current_weather ( location : , unit : "fahrenheit" )
# Here you could use a weather api to fetch the weather.
"The weather in #{ location } is nice ? #{ unit } "
end
messages = [
{
"role" : "user" ,
"content" : "What is the weather like in San Francisco?" ,
} ,
]
response =
client . chat (
parameters : {
model : "gpt-4o" ,
messages : messages , # Defined above because we'll use it again
tools : [
{
type : "function" ,
function : {
name : "get_current_weather" ,
description : "Get the current weather in a given location" ,
parameters : { # Format: https://json-schema.org/understanding-json-schema
type : :object ,
properties : {
location : {
type : :string ,
description : "The city and state, e.g. San Francisco, CA" ,
} ,
unit : {
type : "string" ,
enum : %w[ celsius fahrenheit ] ,
} ,
} ,
required : [ "location" ] ,
} ,
} ,
}
] ,
# Optional, defaults to "auto"
# Can also put "none" or specific functions, see docs
tool_choice : "required"
} ,
)
message = response . dig ( "choices" , 0 , "message" )
if message [ "role" ] == "assistant" && message [ "tool_calls" ]
message [ "tool_calls" ] . each do | tool_call |
tool_call_id = tool_call . dig ( "id" )
function_name = tool_call . dig ( "function" , "name" )
function_args = JSON . parse (
tool_call . dig ( "function" , "arguments" ) ,
{ symbolize_names : true } ,
)
function_response =
case function_name
when "get_current_weather"
get_current_weather ( ** function_args ) # => "The weather is nice ?"
else
# decide how to handle
end
# For a subsequent message with the role "tool", OpenAI requires the preceding message to have a tool_calls argument.
messages << message
messages << {
tool_call_id : tool_call_id ,
role : "tool" ,
name : function_name ,
content : function_response
} # Extend the conversation with the results of the functions
end
second_response = client . chat (
parameters : {
model : "gpt-4o" ,
messages : messages
}
)
puts second_response . dig ( "choices" , 0 , "message" , "content" )
# At this point, the model has decided to call functions, you've called the functions
# and provided the response back, and the model has considered this and responded.
end
# => "It looks like the weather is nice and sunny in San Francisco! If you're planning to go out, it should be a pleasant day."
Appuyez sur l'API OpenAI pour une fin en utilisant d'autres modèles GPT-3:
response = client . completions (
parameters : {
model : "gpt-4o" ,
prompt : "Once upon a time" ,
max_tokens : 5
}
)
puts response [ "choices" ] . map { | c | c [ "text" ] }
# => [", there lived a great"]
Vous pouvez utiliser le point de terminaison des incorporations pour obtenir un vecteur de nombres représentant une entrée. Vous pouvez ensuite comparer ces vecteurs pour différentes entrées pour vérifier efficacement à quel point les entrées sont similaires.
response = client . embeddings (
parameters : {
model : "text-embedding-ada-002" ,
input : "The food was delicious and the waiter..."
}
)
puts response . dig ( "data" , 0 , "embedding" )
# => Vector representation of your embedding
Le point de terminaison des lots vous permet de créer et de gérer de grands lots de demandes d'API pour s'exécuter de manière asynchrone. Actuellement, les points de terminaison pris en charge pour les lots sont /v1/chat/completions
(CHAT EXCELIONS API) et /v1/embeddings
(API Embeddings).
Pour utiliser le point de terminaison des lots, vous devez d'abord télécharger un fichier JSONL contenant les demandes de lots à l'aide du point de terminaison des fichiers. Le fichier doit être téléchargé avec l'objectif défini sur batch
. Chaque ligne du fichier JSONL représente une seule demande et devrait avoir le format suivant:
{
"custom_id" : " request-1 " ,
"method" : " POST " ,
"url" : " /v1/chat/completions " ,
"body" : {
"model" : " gpt-4o " ,
"messages" : [
{ "role" : " system " , "content" : " You are a helpful assistant. " },
{ "role" : " user " , "content" : " What is 2+2? " }
]
}
}
Une fois que vous avez téléchargé le fichier JSONL, vous pouvez créer un nouveau lot en fournissant la fenêtre ID de fichier, point de terminaison et complétion:
response = client . batches . create (
parameters : {
input_file_id : "file-abc123" ,
endpoint : "/v1/chat/completions" ,
completion_window : "24h"
}
)
batch_id = response [ "id" ]
Vous pouvez récupérer des informations sur un lot spécifique à l'aide de son identifiant:
batch = client . batches . retrieve ( id : batch_id )
Pour annuler un lot en cours:
client . batches . cancel ( id : batch_id )
Vous pouvez également énumérer tous les lots:
client . batches . list
Une fois que le lot ["terminé_at"] est présent, vous pouvez récupérer les fichiers de sortie ou d'erreur:
batch = client . batches . retrieve ( id : batch_id )
output_file_id = batch [ "output_file_id" ]
output_response = client . files . content ( id : output_file_id )
error_file_id = batch [ "error_file_id" ]
error_response = client . files . content ( id : error_file_id )
Ces fichiers sont au format JSONL, chaque ligne représentant la sortie ou l'erreur pour une seule demande. Les lignes peuvent être dans n'importe quel ordre:
{
"id" : " response-1 " ,
"custom_id" : " request-1 " ,
"response" : {
"id" : " chatcmpl-abc123 " ,
"object" : " chat.completion " ,
"created" : 1677858242 ,
"model" : " gpt-4o " ,
"choices" : [
{
"index" : 0 ,
"message" : {
"role" : " assistant " ,
"content" : " 2+2 equals 4. "
}
}
]
}
}
Si une demande échoue avec une erreur non-HTTP, l'objet d'erreur contiendra plus d'informations sur la cause de l'échec.
Mettez vos données dans un fichier .jsonl
comme ceci:
{ "prompt" : " Overjoyed with my new phone! -> " , "completion" : " positive " }
{ "prompt" : " @lakers disappoint for a third straight night -> " , "completion" : " negative " }
et passez le chemin (ou un objet StringIO) à client.files.upload
pour le télécharger sur Openai, puis interagir avec lui:
client . files . upload ( parameters : { file : "path/to/sentiment.jsonl" , purpose : "fine-tune" } )
client . files . list
client . files . retrieve ( id : "file-123" )
client . files . content ( id : "file-123" )
client . files . delete ( id : "file-123" )
Vous pouvez envoyer un chemin de fichier:
client . files . upload ( parameters : { file : "path/to/file.pdf" , purpose : "assistants" } )
ou un objet de fichier
my_file = File . open ( "path/to/file.pdf" , "rb" )
client . files . upload ( parameters : { file : my_file , purpose : "assistants" } )
Voir les types de fichiers pris en charge sur la documentation de l'API.
Téléchargez vos données de réglage fin dans un fichier .jsonl
comme ci-dessus et obtenez son identifiant:
response = client . files . upload ( parameters : { file : "path/to/sarcasm.jsonl" , purpose : "fine-tune" } )
file_id = JSON . parse ( response . body ) [ "id" ]
Vous pouvez ensuite utiliser cet ID de fichier pour créer un travail de réglage fin:
response = client . finetunes . create (
parameters : {
training_file : file_id ,
model : "gpt-4o"
} )
fine_tune_id = response [ "id" ]
Cela vous donnera l'identification de tonnerie. Si vous avez fait une erreur, vous pouvez annuler le modèle Fine-tune avant son traitement:
client . finetunes . cancel ( id : fine_tune_id )
Vous devrez peut-être attendre peu de temps pour que le traitement soit terminé. Une fois traité, vous pouvez utiliser la liste ou récupérer pour obtenir le nom du modèle affiné:
client . finetunes . list
response = client . finetunes . retrieve ( id : fine_tune_id )
fine_tuned_model = response [ "fine_tuned_model" ]
Ce nom de modèle affiné peut ensuite être utilisé dans les compléments de chat:
response = client . chat (
parameters : {
model : fine_tuned_model ,
messages : [ { role : "user" , content : "I love Mondays!" } ]
}
)
response . dig ( "choices" , 0 , "message" , "content" )
Vous pouvez également capturer les événements pour un emploi:
client . finetunes . list_events ( id : fine_tune_id )
Les objets de magasin vectoriel donnent à l'outil de recherche de fichiers la possibilité de rechercher vos fichiers.
Vous pouvez créer un nouveau magasin vectoriel:
response = client . vector_stores . create (
parameters : {
name : "my vector store" ,
file_ids : [ "file-abc123" , "file-def456" ]
}
)
vector_store_id = response [ "id" ]
Compte tenu d'un vector_store_id
vous pouvez retrieve
les valeurs de champ actuelles:
client . vector_stores . retrieve ( id : vector_store_id )
Vous pouvez obtenir une list
de tous les magasins vectoriels actuellement disponibles sous l'organisation:
client . vector_stores . list
Vous pouvez modifier une boutique vectorielle existante, à l'exception du file_ids
:
response = client . vector_stores . modify (
id : vector_store_id ,
parameters : {
name : "Modified Test Vector Store" ,
}
)
Vous pouvez supprimer les magasins vectoriels:
client . vector_stores . delete ( id : vector_store_id )
Les fichiers de magasin vectoriel représentent des fichiers dans un magasin vectoriel.
Vous pouvez créer un nouveau fichier de magasin vectoriel en joignant un fichier à un magasin vectoriel.
response = client . vector_store_files . create (
vector_store_id : "vector-store-abc123" ,
parameters : {
file_id : "file-abc123"
}
)
vector_store_file_id = response [ "id" ]
Étant donné un vector_store_file_id
vous pouvez retrieve
les valeurs de champ actuelles:
client . vector_store_files . retrieve (
vector_store_id : "vector-store-abc123" ,
id : vector_store_file_id
)
Vous pouvez obtenir une list
de tous les fichiers de magasin vectoriel actuellement disponibles dans le magasin vectoriel:
client . vector_store_files . list ( vector_store_id : "vector-store-abc123" )
Vous pouvez supprimer un fichier de magasin vectoriel:
client . vector_store_files . delete (
vector_store_id : "vector-store-abc123" ,
id : vector_store_file_id
)
Remarque: Cela supprimera le fichier du magasin vectoriel, mais le fichier lui-même ne sera pas supprimé. Pour supprimer le fichier, utilisez le point de terminaison de suppression.
Les lots de fichiers de magasin vectoriel représentent des opérations pour ajouter plusieurs fichiers à un magasin vectoriel.
Vous pouvez créer un nouveau lot de fichiers de magasin vectoriel en joignant plusieurs fichiers à un magasin vectoriel.
response = client . vector_store_file_batches . create (
vector_store_id : "vector-store-abc123" ,
parameters : {
file_ids : [ "file-abc123" , "file-def456" ]
}
)
file_batch_id = response [ "id" ]
Étant donné un file_batch_id
vous pouvez retrieve
les valeurs de champ actuelles:
client . vector_store_file_batches . retrieve (
vector_store_id : "vector-store-abc123" ,
id : file_batch_id
)
Vous pouvez obtenir une list
de tous les fichiers de magasin vectoriel dans un lot actuellement disponible dans le magasin vectoriel:
client . vector_store_file_batches . list (
vector_store_id : "vector-store-abc123" ,
id : file_batch_id
)
Vous pouvez annuler un lot de fichiers de magasin vectoriel (cela tente d'annuler le traitement des fichiers dans ce lot dès que possible):
client . vector_store_file_batches . cancel (
vector_store_id : "vector-store-abc123" ,
id : file_batch_id
)
Les assistants sont des acteurs avec état qui peuvent avoir de nombreuses conversations et utiliser des outils pour effectuer des tâches (voir Assistant Présentation).
Pour créer un nouvel assistant:
response = client . assistants . create (
parameters : {
model : "gpt-4o" ,
name : "OpenAI-Ruby test assistant" ,
description : nil ,
instructions : "You are a Ruby dev bot. When asked a question, write and run Ruby code to answer the question" ,
tools : [
{ type : "code_interpreter" } ,
{ type : "file_search" }
] ,
tool_resources : {
code_interpreter : {
file_ids : [ ] # See Files section above for how to upload files
} ,
file_search : {
vector_store_ids : [ ] # See Vector Stores section above for how to add vector stores
}
} ,
"metadata" : { my_internal_version_id : "1.0.0" }
}
)
assistant_id = response [ "id" ]
Étant donné un assistant_id
, vous pouvez retrieve
les valeurs de champ actuelles:
client . assistants . retrieve ( id : assistant_id )
Vous pouvez obtenir une list
de tous les assistants actuellement disponibles sous l'organisation:
client . assistants . list
Vous pouvez modifier un assistant existant en utilisant l'ID de l'assistant (voir la documentation de l'API):
response = client . assistants . modify (
id : assistant_id ,
parameters : {
name : "Modified Test Assistant for OpenAI-Ruby" ,
metadata : { my_internal_version_id : '1.0.1' }
}
)
Vous pouvez supprimer les assistants:
client . assistants . delete ( id : assistant_id )
Une fois que vous avez créé un assistant comme décrit ci-dessus, vous devez préparer un Thread
de Messages
sur lequel l'assistant travaille (voir l'introduction sur les assistants). Par exemple, comme une configuration initiale, vous pouvez faire:
# Create thread
response = client . threads . create # Note: Once you create a thread, there is no way to list it
# or recover it currently (as of 2023-12-10). So hold onto the `id`
thread_id = response [ "id" ]
# Add initial message from user (see https://platform.openai.com/docs/api-reference/messages/createMessage)
message_id = client . messages . create (
thread_id : thread_id ,
parameters : {
role : "user" , # Required for manually created messages
content : "Can you help me write an API library to interact with the OpenAI API please?"
}
) [ "id" ]
# Retrieve individual message
message = client . messages . retrieve ( thread_id : thread_id , id : message_id )
# Review all messages on the thread
messages = client . messages . list ( thread_id : thread_id )
Pour nettoyer après qu'un fil n'est plus nécessaire:
# To delete the thread (and all associated messages):
client . threads . delete ( id : thread_id )
client . messages . retrieve ( thread_id : thread_id , id : message_id ) # -> Fails after thread is deleted
Pour soumettre un fil à évaluer avec le modèle d'un assistant, créez une Run
comme suit:
# Create run (will use instruction/model/tools from Assistant's definition)
response = client . runs . create (
thread_id : thread_id ,
parameters : {
assistant_id : assistant_id ,
max_prompt_tokens : 256 ,
max_completion_tokens : 16
}
)
run_id = response [ 'id' ]
Vous pouvez diffuser les morceaux de message au fur et à mesure qu'ils arrivent:
client . runs . create (
thread_id : thread_id ,
parameters : {
assistant_id : assistant_id ,
max_prompt_tokens : 256 ,
max_completion_tokens : 16 ,
stream : proc do | chunk , _bytesize |
if chunk [ "object" ] == "thread.message.delta"
print chunk . dig ( "delta" , "content" , 0 , "text" , "value" )
end
end
}
)
Pour obtenir le statut d'une course:
response = client . runs . retrieve ( id : run_id , thread_id : thread_id )
status = response [ 'status' ]
La réponse status
peut inclure les chaînes suivantes queued
, in_progress
, requires_action
, cancelling
, cancelled
, failed
, completed
ou expired
que vous pouvez gérer comme suit:
while true do
response = client . runs . retrieve ( id : run_id , thread_id : thread_id )
status = response [ 'status' ]
case status
when 'queued' , 'in_progress' , 'cancelling'
puts 'Sleeping'
sleep 1 # Wait one second and poll again
when 'completed'
break # Exit loop and report result to user
when 'requires_action'
# Handle tool calls (see below)
when 'cancelled' , 'failed' , 'expired'
puts response [ 'last_error' ] . inspect
break # or `exit`
else
puts "Unknown status response: #{ status } "
end
end
Si la réponse status
indique que l' run
est completed
, le thread
associé aura un ou plusieurs nouveaux messages
joints:
# Either retrieve all messages in bulk again, or...
messages = client . messages . list ( thread_id : thread_id , parameters : { order : 'asc' } )
# Alternatively retrieve the `run steps` for the run which link to the messages:
run_steps = client . run_steps . list ( thread_id : thread_id , run_id : run_id , parameters : { order : 'asc' } )
new_message_ids = run_steps [ 'data' ] . filter_map do | step |
if step [ 'type' ] == 'message_creation'
step . dig ( 'step_details' , "message_creation" , "message_id" )
end # Ignore tool calls, because they don't create new messages.
end
# Retrieve the individual messages
new_messages = new_message_ids . map do | msg_id |
client . messages . retrieve ( id : msg_id , thread_id : thread_id )
end
# Find the actual response text in the content array of the messages
new_messages . each do | msg |
msg [ 'content' ] . each do | content_item |
case content_item [ 'type' ]
when 'text'
puts content_item . dig ( 'text' , 'value' )
# Also handle annotations
when 'image_file'
# Use File endpoint to retrieve file contents via id
id = content_item . dig ( 'image_file' , 'file_id' )
end
end
end
Vous pouvez également mettre à jour les métadonnées sur les messages, y compris les messages provenant de l'assistant.
metadata = {
user_id : "abc123"
}
message = client . messages . modify (
id : message_id ,
thread_id : thread_id ,
parameters : { metadata : metadata } ,
)
À tout moment, vous pouvez répertorier toutes les exécutions qui ont été effectuées sur un fil particulier ou fonctionnent actuellement:
client . runs . list ( thread_id : thread_id , parameters : { order : "asc" , limit : 3 } )
Vous pouvez également créer un fil et exécuter en un seul appel comme ceci:
response = client . runs . create_thread_and_run ( parameters : { assistant_id : assistant_id } )
run_id = response [ 'id' ]
thread_id = response [ 'thread_id' ]
Vous pouvez inclure des images dans un fil et ils seront décrits et lus par le LLM. Dans cet exemple, j'utilise ce fichier:
require "openai"
# Make a client
client = OpenAI :: Client . new (
access_token : "access_token_goes_here" ,
log_errors : true # Don't log errors in production.
)
# Upload image as a file
file_id = client . files . upload (
parameters : {
file : "path/to/example.png" ,
purpose : "assistants" ,
}
) [ "id" ]
# Create assistant (You could also use an existing one here)
assistant_id = client . assistants . create (
parameters : {
model : "gpt-4o" ,
name : "Image reader" ,
instructions : "You are an image describer. You describe the contents of images." ,
}
) [ "id" ]
# Create thread
thread_id = client . threads . create [ "id" ]
# Add image in message
client . messages . create (
thread_id : thread_id ,
parameters : {
role : "user" , # Required for manually created messages
content : [
{
"type" : "text" ,
"text" : "What's in this image?"
} ,
{
"type" : "image_file" ,
"image_file" : { "file_id" : file_id }
}
]
}
)
# Run thread
run_id = client . runs . create (
thread_id : thread_id ,
parameters : { assistant_id : assistant_id }
) [ "id" ]
# Wait until run in complete
status = nil
until status == "completed" do
sleep ( 0.1 )
status = client . runs . retrieve ( id : run_id , thread_id : thread_id ) [ 'status' ]
end
# Get the response
messages = client . messages . list ( thread_id : thread_id , parameters : { order : 'asc' } )
messages . dig ( "data" , - 1 , "content" , 0 , "text" , "value" )
=> "The image contains a placeholder graphic with a tilted, stylized representation of a postage stamp in the top part, which includes an abstract landscape with hills and a sun. Below the stamp, in the middle of the image, there is italicized text in a light golden color that reads, " This is just an example. " The background is a light pastel shade, and a yellow border frames the entire image."
Dans le cas où vous permettez à l'assistant d'accéder aux outils function
(ils sont définis de la même manière que les fonctions lors de l'achèvement du chat), vous pouvez obtenir un code d'état d' requires_action
lorsque l'assistant souhaite que vous évaluez un ou plusieurs outils de fonction:
def get_current_weather ( location : , unit : "celsius" )
# Your function code goes here
if location =~ /San Francisco/i
return unit == "celsius" ? "The weather is nice ? at 27°C" : "The weather is nice ? at 80°F"
else
return unit == "celsius" ? "The weather is icy ? at -5°C" : "The weather is icy ? at 23°F"
end
end
if status == 'requires_action'
tools_to_call = response . dig ( 'required_action' , 'submit_tool_outputs' , 'tool_calls' )
my_tool_outputs = tools_to_call . map { | tool |
# Call the functions based on the tool's name
function_name = tool . dig ( 'function' , 'name' )
arguments = JSON . parse (
tool . dig ( "function" , "arguments" ) ,
{ symbolize_names : true } ,
)
tool_output = case function_name
when "get_current_weather"
get_current_weather ( ** arguments )
end
{
tool_call_id : tool [ 'id' ] ,
output : tool_output ,
}
}
client . runs . submit_tool_outputs (
thread_id : thread_id ,
run_id : run_id ,
parameters : { tool_outputs : my_tool_outputs }
)
end
Notez que vous avez 10 minutes pour soumettre la sortie de votre outil avant l'expiration de l'exécution.
Prenez une profonde inspiration. Vous pourriez avoir besoin d'un verre pour celui-ci.
Il est possible pour OpenAI de partager les morceaux qu'il a utilisés dans son pipeline de chiffon interne pour créer ses résultats de recherche FileS.
Un exemple de spécification peut être trouvé ici qui fait cela, juste pour que vous sachiez que c'est possible.
Voici comment utiliser les morceaux dans une recherche de fichiers. Dans cet exemple, j'utilise ce fichier:
require "openai"
# Make a client
client = OpenAI :: Client . new (
access_token : "access_token_goes_here" ,
log_errors : true # Don't log errors in production.
)
# Upload your file(s)
file_id = client . files . upload (
parameters : {
file : "path/to/somatosensory.pdf" ,
purpose : "assistants"
}
) [ "id" ]
# Create a vector store to store the vectorised file(s)
vector_store_id = client . vector_stores . create ( parameters : { } ) [ "id" ]
# Vectorise the file(s)
vector_store_file_id = client . vector_store_files . create (
vector_store_id : vector_store_id ,
parameters : { file_id : file_id }
) [ "id" ]
# Check that the file is vectorised (wait for status to be "completed")
client . vector_store_files . retrieve ( vector_store_id : vector_store_id , id : vector_store_file_id ) [ "status" ]
# Create an assistant, referencing the vector store
assistant_id = client . assistants . create (
parameters : {
model : "gpt-4o" ,
name : "Answer finder" ,
instructions : "You are a file search tool. Find the answer in the given files, please." ,
tools : [
{ type : "file_search" }
] ,
tool_resources : {
file_search : {
vector_store_ids : [ vector_store_id ]
}
}
}
) [ "id" ]
# Create a thread with your question
thread_id = client . threads . create ( parameters : {
messages : [
{ role : "user" ,
content : "Find the description of a nociceptor." }
]
} ) [ "id" ]
# Run the thread to generate the response. Include the "GIVE ME THE CHUNKS" incantation.
run_id = client . runs . create (
thread_id : thread_id ,
parameters : {
assistant_id : assistant_id
} ,
query_parameters : { include : [ "step_details.tool_calls[*].file_search.results[*].content" ] } # incantation
) [ "id" ]
# Get the steps that happened in the run
steps = client . run_steps . list (
thread_id : thread_id ,
run_id : run_id ,
parameters : { order : "asc" }
)
# Retrieve all the steps. Include the "GIVE ME THE CHUNKS" incantation again.
steps = steps [ "data" ] . map do | step |
client . run_steps . retrieve (
thread_id : thread_id ,
run_id : run_id ,
id : step [ "id" ] ,
parameters : { include : [ "step_details.tool_calls[*].file_search.results[*].content" ] } # incantation
)
end
# Now we've got the chunk info, buried deep. Loop through the steps and find chunks if included:
chunks = steps . flat_map do | step |
included_results = step . dig ( "step_details" , "tool_calls" , 0 , "file_search" , "results" )
next if included_results . nil? || included_results . empty?
included_results . flat_map do | result |
result [ "content" ] . map do | content |
content [ "text" ]
end
end
end . compact
# The first chunk will be the closest match to the prompt. Finally, if you want to view the completed message(s):
client . messages . list ( thread_id : thread_id )
Générez des images à l'aide de Dall · E 2 ou Dall · E 3!
Pour Dall · E 2, la taille de toutes les images générées doit être l'une des 256x256
, 512x512
ou 1024x1024
- si elle n'est pas spécifiée, l'image sera par défaut 1024x1024
.
response = client . images . generate (
parameters : {
prompt : "A baby sea otter cooking pasta wearing a hat of some sort" ,
size : "256x256" ,
}
)
puts response . dig ( "data" , 0 , "url" )
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
Pour Dall · E 3, la taille de toutes les images générées doit être l'une des 1024x1024
, 1024x1792
ou 1792x1024
. De plus, la qualité de l'image peut être spécifiée en standard
ou hd
.
response = client . images . generate (
parameters : {
prompt : "A springer spaniel cooking pasta wearing a hat of some sort" ,
model : "dall-e-3" ,
size : "1024x1792" ,
quality : "standard" ,
}
)
puts response . dig ( "data" , 0 , "url" )
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
Remplissez la partie transparente d'une image ou téléchargez un masque avec des sections transparentes pour indiquer les parties d'une image qui peut être modifiée en fonction de votre invite ...
response = client . images . edit (
parameters : {
prompt : "A solid red Ruby on a blue background" ,
image : "image.png" ,
mask : "mask.png" ,
}
)
puts response . dig ( "data" , 0 , "url" )
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
Créez n variations d'une image.
response = client . images . variations ( parameters : { image : "image.png" , n : 2 } )
puts response . dig ( "data" , 0 , "url" )
# => "https://oaidalleapiprodscus.blob.core.windows.net/private/org-Rf437IxKhh..."
Passez une chaîne pour vérifier si elle viole la politique de contenu d'Openai:
response = client . moderations ( parameters : { input : "I'm worried about that." } )
puts response . dig ( "results" , 0 , "category_scores" , "hate" )
# => 5.505014632944949e-05
Whisper est un modèle de discours à texte qui peut être utilisé pour générer du texte basé sur des fichiers audio:
L'API de traductions prend en entrée le fichier audio dans l'une des langues prises en charge et transcrit l'audio en anglais.
response = client . audio . translate (
parameters : {
model : "whisper-1" ,
file : File . open ( "path_to_file" , "rb" ) ,
}
)
puts response [ "text" ]
# => "Translation of the text"
L'API Transcriptions prend en entrée le fichier audio que vous souhaitez transcrire et renvoie le texte au format de fichier de sortie souhaité.
Vous pouvez passer la langue du fichier audio pour améliorer la qualité de la transcription. Les langues prises en charge sont répertoriées ici. Vous devez fournir la langue en tant que code ISO-639-1, par exemple. "en" pour l'anglais ou "NE" pour Népalais. Vous pouvez rechercher les codes ici.
response = client . audio . transcribe (
parameters : {
model : "whisper-1" ,
file : File . open ( "path_to_file" , "rb" ) ,
language : "en" , # Optional
}
)
puts response [ "text" ]
# => "Transcription of the text"
L'API de la parole prend en entrée le texte et une voix et renvoie le contenu d'un fichier audio que vous pouvez écouter.
response = client . audio . speech (
parameters : {
model : "tts-1" ,
input : "This is a speech test!" ,
voice : "alloy" ,
response_format : "mp3" , # Optional
speed : 1.0 , # Optional
}
)
File . binwrite ( 'demo.mp3' , response )
# => mp3 file that plays: "This is a speech test!"
Les erreurs HTTP peuvent être capturées comme ceci:
begin
OpenAI :: Client . new . models . retrieve ( id : "gpt-4o" )
rescue Faraday :: Error => e
raise "Got a Faraday error: #{ e } "
end
Après avoir vérifié le dépôt, exécutez bin/setup
pour installer des dépendances. Vous pouvez exécuter bin/console
pour une invite interactive qui vous permettra d'expérimenter.
Pour installer ce gemme sur votre machine locale, exécutez bundle exec rake install
.
Pour exécuter tous les tests, exécutez le bundle exec rake
de commandes, qui exécutera également le linter (RuboCop). Ce référentiel utilise du magnétoscope pour enregistrer les demandes d'API.
Avertissement
Si vous avez un OPENAI_ACCESS_TOKEN
dans votre ENV
, l'exécution des spécifications l'utilisera pour exécuter les spécifications par rapport à l'API réelle, ce qui sera lent et vous coûtera de l'argent - 2 cents ou plus! Supprimez-le de votre environnement avec unset
ou similaire si vous souhaitez simplement exécuter les spécifications par rapport aux réponses du magnétoscope stockées.
Exécutez d'abord les spécifications sans magnétoscope, ils ont donc frappé l'API. Cela coûtera 2 cents ou plus. Définissez Openai_Access_Token dans votre environnement ou passez-le comme ceci:
OPENAI_ACCESS_TOKEN=123abc bundle exec rspec
Ensuite, mettez à jour le numéro de version dans version.rb
, mettez à jour CHANGELOG.md
, exécutez bundle install
pour mettre à jour GEMFile.lock, puis exécutez bundle exec rake release
, qui créera une balise Git pour la version, pousse Git Commits and Tags, et pousse Le fichier .gem
à RubyGems.org.
Les rapports de bogues et les demandes de traction sont les bienvenus sur GitHub sur https://github.com/alexrudall/ruby-openai. Ce projet est destiné à être un espace sûr et accueillant pour la collaboration, et les contributeurs devraient adhérer au code de conduite.
Le GEM est disponible en open source en vertu des termes de la licence du MIT.
Tout le monde interagit dans les bases de code, les trackers de problèmes, les salles de chat et les listes de diffusion du Ruby Openai devrait suivre le code de conduite.