Bienvenue dans le référentiel d'exemples pour intégrer des copilotes personnalisés dans le terminal OpenBB.
Ce référentiel fournit tout ce dont vous avez besoin pour créer et ajouter vos propres copilotes personnalisés à OpenBB Copilot.
Voici quelques raisons courantes pour lesquelles vous pourriez vouloir créer votre propre copilote :
Pour intégrer un Copilot personnalisé avec lequel vous pouvez interagir depuis le terminal OpenBB, vous devrez créer une API backend à laquelle le terminal peut adresser des requêtes.
Votre API copilote personnalisée répondra avec des événements envoyés par le serveur (SSE).
Remarque : Si vous souhaitez démarrer rapidement, nous vous suggérons d'exécuter l'un des exemples de copilotes inclus dans ce référentiel et de l'ajouter en tant que copilote personnalisé au terminal OpenBB (chaque exemple de copilote comprend des instructions sur la façon de les exécuter). Cloner et modifier un exemple de copilote est un excellent moyen de créer un copilote personnalisé.
Le concept le plus important à comprendre est que le protocole copilote est apatride . Cela signifie que chaque requête du terminal OpenBB à votre copilote inclura tous les messages précédents (tels que les achèvements de l'IA, les messages humains, les appels de fonction et les résultats des appels de fonction) dans la charge utile de la requête.
Cela signifie qu'il n'est pas nécessaire que votre copilote personnalisé conserve un état entre les requêtes. Il doit simplement utiliser la charge utile de la requête pour générer une réponse.
OpenBB Terminal est seul responsable du maintien de l'état de la conversation et ajoutera les réponses au tableau messages
dans la charge utile de la requête.
OpenBB Terminal enverra des requêtes POST au point de terminaison query
défini dans votre fichier copilots.json
(nous en parlerons plus tard). La charge utile de cette requête contiendra des données telles que les messages de la conversation en cours, tout contexte explicitement ajouté, des informations sur les widgets du tableau de bord actuellement actif, les URL à récupérer, etc.
Le cœur du schéma de requête de requête que vous devez implémenter est le suivant :
{
"messages" : [ # <-- the chat messages between the user and the copilot (including function calls and results)
{
"role" : "human" , # <-- each message has a role: "human", "ai", or "tool"
"content" : "Hi there." # <-- the content of the message
}
],
"context" : [ # <-- explicitly added context by the user (optional)
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- the UUID of the widget
"name" : "<widget name>" , # <-- the name of the widget
"description" : "<widget description>" , # <-- the description of the widget
"data" : {
"content" : "<data>" # <-- the data of the widget
},
"metadata" : {
"<metadata key>" : "<metadata value>" , # <-- the metadata of the widget
...
}
},
...
],
"widgets" : [ # <-- the widgets currently visible on the active dashboard on Terminal Pro (optional)
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- the UUID of the widget
"name" : "<widget name>" , # <-- the name of the widget
"description" : "<widget description>" , # <-- the description of the widget
"metadata" : {
"<metadata key>" : "<metadata value>" , # <-- the metadata of the widget
...
}
},
...
],
}
Nous examinerons chacun de ces champs plus en détail ci-dessous.
messages
Il s'agit de la liste des messages entre l'utilisateur et le copilote. Cela inclut les messages de l'utilisateur, les messages du copilote, les appels de fonction et les résultats des appels de fonction. Chaque message a un role
et content
.
L'exemple le plus simple est celui où aucun appel de fonction n'est impliqué, qui consiste simplement en un tableau de messages human
et ai
.
OpenBB Terminal ajoute automatiquement tous les messages ai
de retour (de votre Copilot) au tableau messages
de toute demande de suivi.
# Only one human message
{
"messages" : [
{
"role" : "human" ,
"content" : "Hi there."
}
],
...
}
# Multiple messages
{
"messages" : [
{
"role" : "human" ,
"content" : "Hi there."
},
{
"role" : "ai" ,
"content" : "Hi there, I'm a copilot. How are you?"
},
{
"role" : "human" ,
"content" : "I'm fine, thank you. What is the weather in Tokyo?"
}
],
...
}
Les appels de fonction vers Terminal Pro (par exemple lors de la récupération des données du widget), ainsi que les résultats de ces appels de fonction (contenant les données du widget), sont également inclus dans le tableau messages
. Pour plus d’informations sur l’appel de fonction, consultez la section « Appel de fonction » ci-dessous.
context
Il s'agit d'un tableau facultatif de données de widget qui sera envoyé par le terminal OpenBB lorsque les widgets auront été ajoutés explicitement en tant que contexte par l'utilisateur. Cela se produit lorsque l'utilisateur clique sur le bouton "Ajouter comme contexte" sur un widget dans le terminal OpenBB.
Le champ context
fonctionne comme suit :
{
...
" context ": [
{
"uuid" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" , # <-- each widget has a UUID
"name" : "Analyst Estimates" ,
"description" : "Contains analyst estimates for a ticker" ,
"data" : {
"content" : "<data>" # <-- the data of the widget could either be a JSON string or plaintext (you must choose how to handle this in your copilot)
},
"metadata" : { # <-- additional metadata about the widget
"symbol" : "AAPL" ,
"period" : "quarter" ,
"source" : "Financial Modelling Prep" ,
"lastUpdated" : 1728998071322
}
},
{
"uuid" : "8b2e5f79-3a1d-4c9e-b6f8-1e7d2a9c0b3d" , # <-- the context can have multiple widgets
"name" : "Earnings Transcripts" ,
"description" : "Contains earnings transcripts for a ticker" ,
"data" : {
"content" : "<data>" # <-- the data of the widget
},
"metadata" : {
"symbol" : "AAPL" ,
"period" : "quarter" ,
"source" : "Intrinio" ,
"lastUpdated" : 1728998071322
}
},
...
],
...
}
widgets
Il s'agit d'un tableau de widgets actuellement visibles sur le tableau de bord actif de Terminal Pro. * Ceci n'est utile que si vous envisagez d'implémenter un appel de fonction dans votre copilote personnalisé (ce qui est recommandé, mais pas obligatoire), ce qui lui permet de demander des données de widget au tableau de bord actuellement actif de l'utilisateur sur le terminal OpenBB.
{
...
" widgets ": [
{
"uuid" : "c276369e-e469-4689-b5fe-3f8c76f7c45a" ,
"name" : "Stock Price Quote Widget" ,
"description" : "Contains the current stock price of a ticker" ,
"metadata" : {
"ticker" : "AAPL"
}
},
{
"uuid" : "9f8e7d6c-5b4a-3c2e-1d0f-9e8d7c6b5a4b" ,
"name" : "Financial Ratios Widget" ,
"description" : "Displays key financial ratios for a company" ,
"metadata" : {
"ticker" : "AAPL" ,
"period" : "TTM"
}
},
...
],
...
}
Votre copilote personnalisé doit répondre à la demande du terminal OpenBB à l'aide d'événements envoyés par le serveur (SSE).
OpenBB Terminal peut traiter les SSE suivants :
copilotMessageChunk
: utilisé pour renvoyer les jetons de copilote diffusés en continu (réponses partielles) au terminal OpenBB. Ces réponses peuvent être diffusées au fur et à mesure de leur génération.copilotFunctionCall
: utilisé pour demander des données (par exemple, des données de widget) ou exécuter une fonction spécifique. Cela demande à Terminal Pro de prendre d'autres mesures du côté du client. Cela n'est nécessaire que si vous envisagez d'implémenter des appels de fonctions dans votre copilote personnalisé. copilotMessageChunk
Le fragment de message SSE a le format suivant :
event: copilotMessageChunk
data: {"delta":"H"} # <-- the `data` field must be a JSON object.
Le delta
doit être une chaîne, mais peut avoir n'importe quelle longueur. Nous vous suggérons de diffuser chaque morceau que vous recevez de votre LLM dès qu'il est généré sous forme de delta
.
Par exemple, si vous souhaitez renvoyer le message « Salut ! », vous enverrez les SSE suivants :
event: copilotMessageChunk
data: {"delta":"H"}
event: copilotMessageChunk
data: {"delta":"i"}
event: copilotMessageChunk
data: {"delta":"!"}
copilotFunctionCall
(uniquement requis pour l'appel de fonction)L'appel de fonction SSE a le format suivant :
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Encore une fois, le champ data
doit être un objet JSON. Le champ function
est le nom de la fonction à appeler (actuellement, seul get_widget_data
est pris en charge), et le champ input_arguments
est un dictionnaire d'arguments à transmettre à la fonction. Pour la fonction get_widget_data
, le seul argument obligatoire est widget_uuid
, qui est l'UUID du widget pour lequel récupérer les données (à partir de l'un des UUID du tableau widgets
de la requête).
En ajoutant des appels de fonction à votre copilote, il pourra demander des données visibles sur le tableau de bord actuellement actif d'un utilisateur dans OpenBB Terminal.
Une liste de tous les widgets actuellement visibles sur le tableau de bord d'un utilisateur est envoyée à votre copilote dans le tableau widgets
de la charge utile de la requête.
Pour récupérer les données d'un widget, votre copilote doit répondre avec un événement copilotFunctionCall
, en spécifiant l'UUID du widget :
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Après avoir émis un événement copilotFunctionCall
, vous devez fermer la connexion et attendre une nouvelle demande de requête du terminal OpenBB.
Lorsqu'un événement copilotFunctionCall
est reçu, OpenBB Terminal récupérera les données et lancera une nouvelle demande de requête. Cette nouvelle requête de requête inclura l'appel de fonction d'origine, ainsi que le résultat de l'appel de fonction dans le tableau messages
.
{
...
" messages ": [
...
{
"role" : "ai" ,
"content" : "{ " function " : " get_widget_data " , " input_arguments " :{ " widget_uuid " : " c276369e-e469-4689-b5fe-3f8c76f7c45a " }}"
},
{
"role" : "tool" ,
"function" : "get_widget_data" ,
"data" : {
"content" : "<data>"
}
}
]
}
Notez que :
messages
.content
du message ai
d'appel de fonction est un objet JSON codé en chaîne textuelle du champ data
de l'événement copilotFunctionCall
(il s'agit d'un mécanisme très utile pour faire passer clandestinement des métadonnées supplémentaires liées à l'appel de fonction, si votre copilote en a besoin). Actuellement, le seul appel de fonction pris en charge par le terminal OpenBB est get_widget_data
, qui récupère les données d'un widget spécifique.
Votre copilote personnalisé reçoit la requête suivante du terminal OpenBB :
{
"messages" : [
{
"role" : " human " ,
"content" : " What is the current stock price of AAPL? "
}
],
"widgets" : [
{
"uuid" : " 38181a68-9650-4940-84fb-a3f29c8869f3 " ,
"name" : " Historical Stock Price " ,
"description" : " Historical Stock Price " ,
"metadata" : {
"symbol" : " AAPL " ,
"source" : " Financial Modelling Prep " ,
"lastUpdated" : 1728994470324
}
}
]
}
Vous analysez ensuite la réponse, formatez les messages dans votre LLM (y compris les informations sur les widgets disponibles). Supposons que votre copilote détermine que la requête de l'utilisateur peut recevoir une réponse à l'aide du widget disponible et génère un appel de fonction pour récupérer les données.
Votre copilote répond alors avec le SSE suivant et ferme la connexion :
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"38181a68-9650-4940-84fb-a3f29c8869f3"}}
OpenBB Terminal exécutera alors la fonction spécifiée et fera une nouvelle requête de requête à votre copilote personnalisé :
{
"messages" : [
{
"role" : "human" ,
"content" : "What is the current stock price of AAPL?"
},
{
"role" : "ai" ,
"content" : "{ " function " : " get_widget_data " , " input_arguments " :{ " widget_uuid " : " 38181a68-9650-4940-84fb-a3f29c8869f3 " }}"
},
{
"role" : "tool" ,
"function" : "get_widget_data" ,
"data" : {
"content" : "[{ " date " : " 2024-10-15T00:00:00-04:00 " , " open " :233.61, " high " :237.49, " low " :232.37, " close " :233.85, " volume " :61901688, " vwap " :234.33, " adj_close " :233.85, " change " :0.24, " change_percent " :0.0010274},{ " date " : " 2024-10-14T00:00:00-04:00 " , " open " :228.7, " high " :231.73, " low " :228.6, " close " :231.3, " volume " :39882100, " vwap " :230.0825, " adj_close " :231.3, " change " :2.6, " change_percent " :0.0114},{ " date " : " 2024-10-11T00:00:00-04:00 " , " open " :229.3, " high " :233.2, " low " :228.9, " close " :231.0, " volume " :32581944, " vwap " :231.0333, " adj_close " :231.0, " change " :1.7, " change_percent " :0.0074}, ... ]"
}
}
],
"widgets" : [
{
"uuid" : "38181a68-9650-4940-84fb-a3f29c8869f3" ,
"name" : "Historical Stock Price" ,
"description" : "Historical Stock Price" ,
"metadata" : {
"symbol" : "AAPL" ,
"source" : "Financial Modelling Prep" ,
"lastUpdated" : 1728994470324
}
}
}
Vous analysez ensuite la réponse, traitez les données et formatez les messages dans votre LLM. Supposons que le LLM génère ensuite une chaîne de jetons pour répondre à la requête de l'utilisateur. Ceux-ci sont ensuite renvoyés à l'utilisateur à l'aide du copilotMessageChunk
SSE :
event: copilotMessageChunk
data: {"delta":"The"}
event: copilotMessageChunk
data: {"delta":" current"}
event: copilotMessageChunk
data: {"delta":" stock"}
event: copilotMessageChunk
data: {"delta":" price"}
event: copilotMessageChunk
data: {"delta":" of"}
event: copilotMessageChunk
data: {"delta":" Apple"}
event: copilotMessageChunk
data: {"delta":" Inc."}
event: copilotMessageChunk
data: {"delta":" (AAPL)"}
event: copilotMessageChunk
data: {"delta":" is"}
event: copilotMessageChunk
data: {"delta":" $150.75."}
copilots.json
) Pour intégrer votre copilote personnalisé avec OpenBB Terminal, vous devez configurer et servir un fichier copilots.json
. Ce fichier définit la manière dont votre copilote personnalisé se connecte au frontend, y compris les fonctionnalités prises en charge par votre copilote personnalisé et où les demandes doivent être envoyées.
Voici un exemple de configuration copilots.json :
{
"example_copilot" : { # <-- the ID of your copilot
"name" : "Mistral Example Co. Copilot" , # <-- the display name of your copilot
"description" : "AI-powered financial copilot that uses Mistral Large as its LLM." , # <-- a short description of your copilot
"image" : "<url>" , # <-- a URL to an image icon for your copilot
"hasStreaming" : true , # <-- whether your copilot supports streaming responses via SSEs. This must always be true.
"hasFunctionCalling" : true , # <-- whether your copilot supports function calling
"endpoints" : {
"query" : "<url>" # <-- the URL that Terminal Pro will send requests to. For example, "http://localhost:7777/v1/query"
}
}
}
Votre fichier copilots.json
doit être servi sur <your-host>/copilots.json
, par exemple, http://localhost:7777/copilots.json
.