Bienvenido al repositorio de ejemplo para integrar copilotos personalizados en la Terminal OpenBB.
Este repositorio proporciona todo lo que necesita para crear y agregar sus propios copilotos personalizados a OpenBB Copilot.
A continuación se presentan algunas razones comunes por las que es posible que desee crear su propio copiloto:
Para integrar un Copilot personalizado con el que pueda interactuar desde la Terminal OpenBB, deberá crear una API de backend a la que la Terminal pueda realizar solicitudes.
Su API de copiloto personalizada responderá con eventos enviados por el servidor (SSE).
Nota: Si desea comenzar rápidamente, le sugerimos ejecutar uno de los copilotos de ejemplo incluidos como parte de este repositorio y agregarlo como copiloto personalizado a OpenBB Terminal (cada copiloto de ejemplo incluye instrucciones sobre cómo ejecutarlos). Clonar y modificar un copiloto de ejemplo es una excelente manera de crear un copiloto personalizado.
El concepto más importante que hay que entender es que el protocolo copiloto no tiene estado . Esto significa que cada solicitud de la Terminal OpenBB a su copiloto incluirá todos los mensajes anteriores (como finalizaciones de IA, mensajes humanos, llamadas a funciones y resultados de llamadas a funciones) en la carga útil de la solicitud.
Esto significa que no es necesario que su copiloto personalizado mantenga ningún estado entre solicitudes. Simplemente debería usar la carga útil de la solicitud para generar una respuesta.
OpenBB Terminal es el único responsable de mantener el estado de la conversación y agregará las respuestas a la matriz messages
en la carga útil de la solicitud.
OpenBB Terminal realizará solicitudes POST al punto final query
definido en su archivo copilots.json
(más sobre esto más adelante). La carga útil de esta solicitud contendrá datos como los mensajes de la conversación actual, cualquier contexto agregado explícitamente, información sobre los widgets en el panel actualmente activo, URL para recuperar, etc.
El núcleo del esquema de solicitud de consulta que debe implementar es el siguiente:
{
"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
...
}
},
...
],
}
A continuación repasaremos cada uno de estos campos con más detalle.
messages
Esta es la lista de mensajes entre el usuario y el copiloto. Esto incluye los mensajes del usuario, los mensajes del copiloto, las llamadas a funciones y los resultados de las llamadas a funciones. Cada mensaje tiene una role
y content
.
El ejemplo más simple es cuando no se trata de ninguna llamada a función, que simplemente consiste en una serie de mensajes human
y ai
.
OpenBB Terminal agrega automáticamente todos los mensajes ai
respuesta (de su Copilot) a la matriz de messages
de cualquier solicitud de seguimiento.
# 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?"
}
],
...
}
Las llamadas a funciones a Terminal Pro (como cuando se recuperan datos del widget), así como los resultados de esas llamadas a funciones (que contienen los datos del widget), también se incluyen en la matriz messages
. Para obtener información sobre llamadas a funciones, consulte la sección "Llamadas a funciones" a continuación.
context
Esta es una matriz opcional de datos de widgets que OpenBB Terminal enviará cuando los widgets hayan sido agregados explícitamente como contexto por el usuario. Esto sucede cuando el usuario hace clic en el botón "Agregar como contexto" en un widget en la Terminal OpenBB.
El campo context
funciona de la siguiente manera:
{
...
" 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
Se trata de una serie de widgets que actualmente están visibles en el panel activo de Terminal Pro. * Esto solo es útil si planea implementar llamadas a funciones en su copiloto personalizado (lo cual se recomienda, pero no es obligatorio), lo que le permite solicitar datos del widget desde el panel de control actualmente activo del usuario en la 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"
}
},
...
],
...
}
Su copiloto personalizado debe responder a la solicitud de la Terminal OpenBB utilizando Eventos enviados por el servidor (SSE).
OpenBB Terminal puede procesar los siguientes SSE:
copilotMessageChunk
: se utiliza para devolver tokens de copiloto transmitidos (respuestas parciales) a la terminal OpenBB. Estas respuestas se pueden transmitir a medida que se generan.copilotFunctionCall
: Se utiliza para solicitar datos (por ejemplo, datos de widgets) o realizar una función específica. Esto indica a Terminal Pro que tome medidas adicionales por parte del cliente. Esto solo es necesario si planea implementar llamadas a funciones en su copiloto personalizado. copilotMessageChunk
El fragmento de mensaje SSE tiene el siguiente formato:
event: copilotMessageChunk
data: {"delta":"H"} # <-- the `data` field must be a JSON object.
El delta
debe ser una cadena, pero puede tener cualquier longitud. Le sugerimos transmitir cada fragmento que reciba de su LLM tan pronto como se genere como un delta
.
Por ejemplo, si quisiera transmitir el mensaje "¡Hola!", enviaría los siguientes SSE:
event: copilotMessageChunk
data: {"delta":"H"}
event: copilotMessageChunk
data: {"delta":"i"}
event: copilotMessageChunk
data: {"delta":"!"}
copilotFunctionCall
(solo necesario para llamar a funciones)La llamada a función SSE tiene el siguiente formato:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Nuevamente, el campo data
debe ser un objeto JSON. El campo function
es el nombre de la función que se llamará (actualmente solo se admite get_widget_data
) y el campo input_arguments
es un diccionario de argumentos que se pasarán a la función. Para la función get_widget_data
, el único argumento requerido es widget_uuid
, que es el UUID del widget para recuperar datos (de uno de los UUID en la matriz de widgets
de la solicitud).
Al agregar llamadas de función a su copiloto, podrá solicitar datos que sean visibles en el panel de control actualmente activo de un usuario en la Terminal OpenBB.
Se envía una lista de todos los widgets actualmente visibles en el panel de un usuario a su copiloto en la matriz de widgets
de la carga útil de la solicitud.
Para recuperar los datos de un widget, su copiloto debe responder con un evento copilotFunctionCall
, especificando el UUID del widget:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Después de emitir un evento copilotFunctionCall
, debe cerrar la conexión y esperar una nueva solicitud de consulta desde OpenBB Terminal.
Cuando se recibe un evento copilotFunctionCall
, OpenBB Terminal recuperará los datos e iniciará una nueva solicitud de consulta. Esta nueva solicitud de consulta incluirá la llamada a la función original, así como el resultado de la llamada a la función en la matriz 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>"
}
}
]
}
Note que:
messages
.content
del mensaje ai
de llamada a función es un objeto JSON textualmente codificado en cadena del campo data
del evento copilotFunctionCall
(este es un mecanismo muy útil para contrabandear metadatos adicionales relacionados con la llamada a función, si su copiloto lo necesita). Actualmente, la única llamada de función admitida por la Terminal OpenBB es get_widget_data
, que recupera datos de un widget específico.
Su copiloto personalizado recibe la siguiente solicitud de OpenBB Terminal:
{
"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
}
}
]
}
Luego, analiza la respuesta y formatea los mensajes en su LLM (incluida información sobre qué widgets están disponibles). Supongamos que su copiloto determina que la consulta del usuario se puede responder utilizando el widget disponible y genera una llamada de función para recuperar los datos.
Su copiloto luego responde con el siguiente SSE y cierra la conexión:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"38181a68-9650-4940-84fb-a3f29c8869f3"}}
OpenBB Terminal luego ejecutará la función especificada y realizará una nueva solicitud de consulta a su copiloto personalizado:
{
"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
}
}
}
Luego analiza la respuesta, procesa los datos y formatea los mensajes en su LLM. Supongamos que el LLM genera una cadena de tokens para responder a la consulta del usuario. Luego, estos se transmiten al usuario mediante 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
) Para integrar su copiloto personalizado con OpenBB Terminal, necesita configurar y servir un archivo copilots.json
. Este archivo define cómo se conecta su copiloto personalizado con la interfaz, incluidas qué funciones admite su copiloto personalizado y dónde se deben enviar las solicitudes.
A continuación se muestra un ejemplo de configuración de copilotos.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"
}
}
}
Su archivo copilots.json
debe entregarse en <your-host>/copilots.json
, por ejemplo, http://localhost:7777/copilots.json
.