Bem-vindo ao repositório de exemplos para integração de copilotos personalizados no Terminal OpenBB.
Este repositório fornece tudo que você precisa para construir e adicionar seus próprios copilotos personalizados ao OpenBB Copilot.
Aqui estão alguns motivos comuns pelos quais você pode querer construir seu próprio copiloto:
Para integrar um Copilot personalizado com o qual você pode interagir a partir do Terminal OpenBB, você precisará criar uma API de back-end para a qual o Terminal possa fazer solicitações.
Sua API de copiloto personalizada responderá com eventos enviados pelo servidor (SSEs).
Observação: se você deseja começar rapidamente, sugerimos executar um dos copilotos de exemplo incluídos como parte deste repositório e adicioná-lo como um copiloto personalizado ao Terminal OpenBB (cada copiloto de exemplo inclui instruções sobre como executá-los). Clonar e modificar um copiloto de exemplo é uma ótima maneira de criar um copiloto customizado.
O conceito mais importante a ser entendido é que o protocolo copiloto não tem estado . Isso significa que cada solicitação do Terminal OpenBB para o seu copiloto incluirá todas as mensagens anteriores (como conclusões de IA, mensagens humanas, chamadas de função e resultados de chamadas de função) na carga útil da solicitação.
Isso significa que não é necessário que seu copiloto customizado mantenha qualquer estado entre as solicitações. Ele deve simplesmente usar a carga útil da solicitação para gerar uma resposta.
O Terminal OpenBB é o único responsável por manter o estado da conversa e anexará as respostas ao array messages
na carga útil da solicitação.
O Terminal OpenBB fará solicitações POST para o endpoint query
definido em seu arquivo copilots.json
(mais sobre isso mais tarde). A carga desta solicitação conterá dados como as mensagens da conversa atual, qualquer contexto adicionado explicitamente, informações sobre widgets no painel atualmente ativo, URLs a serem recuperadas e assim por diante.
O núcleo do esquema de solicitação de consulta que você deve implementar é o seguinte:
{
"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
...
}
},
...
],
}
Examinaremos cada um desses campos com mais detalhes a seguir.
messages
Esta é a lista de mensagens entre o usuário e o copiloto. Isso inclui as mensagens do usuário, as mensagens do copiloto, chamadas de função e resultados de chamadas de função. Cada mensagem tem uma role
e content
.
O exemplo mais simples é quando nenhuma chamada de função está envolvida, que consiste simplesmente em uma matriz de mensagens human
e ai
.
O Terminal OpenBB anexa automaticamente todas as mensagens de retorno ai
(do seu Copilot) ao array messages
de qualquer solicitação de acompanhamento.
# 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?"
}
],
...
}
As chamadas de função para o Terminal Pro (como ao recuperar dados do widget), bem como os resultados dessas chamadas de função (contendo os dados do widget), também são incluídos na matriz messages
. Para obter informações sobre chamada de função, consulte a seção "Chamada de função" abaixo.
context
Este é um array opcional de dados de widget que será enviado pelo Terminal OpenBB quando os widgets forem adicionados explicitamente como contexto pelo usuário. Isso acontece quando o usuário clica no botão “Adicionar como Contexto” em um widget do Terminal OpenBB.
O campo context
funciona da seguinte maneira:
{
...
" 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
Esta é uma série de widgets que estão atualmente visíveis no painel ativo do Terminal Pro. * Isso só é útil se você estiver planejando implementar chamadas de função em seu copiloto personalizado (o que é recomendado, mas não obrigatório), o que permite solicitar dados de widget do painel atualmente ativo do usuário no 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"
}
},
...
],
...
}
Seu copiloto personalizado deve responder à solicitação do Terminal OpenBB usando Server-Sent Events (SSEs).
O Terminal OpenBB pode processar os seguintes SSEs:
copilotMessageChunk
: usado para retornar tokens de copiloto transmitidos (respostas parciais) de volta ao Terminal OpenBB. Essas respostas podem ser transmitidas à medida que são geradas.copilotFunctionCall
: Usado para solicitar dados (por exemplo, dados de widget) ou executar uma função específica. Isso instrui o Terminal Pro a tomar outras medidas por parte do cliente. Isso só será necessário se você estiver planejando implementar chamadas de função em seu copiloto personalizado. copilotMessageChunk
O bloco de mensagem SSE tem o seguinte formato:
event: copilotMessageChunk
data: {"delta":"H"} # <-- the `data` field must be a JSON object.
O delta
deve ser uma string, mas pode ter qualquer comprimento. Sugerimos transmitir de volta cada pedaço que você recebe do seu LLM assim que ele for gerado como um delta
.
Por exemplo, se você quiser transmitir a mensagem "Olá!", envie os seguintes SSEs:
event: copilotMessageChunk
data: {"delta":"H"}
event: copilotMessageChunk
data: {"delta":"i"}
event: copilotMessageChunk
data: {"delta":"!"}
copilotFunctionCall
(necessário apenas para chamada de função)A chamada de função SSE tem o seguinte formato:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Novamente, o campo data
deve ser um objeto JSON. O campo function
é o nome da função a ser chamada (atualmente apenas get_widget_data
é suportado) e o campo input_arguments
é um dicionário de argumentos a serem passados para a função. Para a função get_widget_data
, o único argumento necessário é widget_uuid
, que é o UUID do widget para o qual recuperar dados (de um dos UUIDs na matriz de widgets
da solicitação).
Ao adicionar chamada de função ao seu copiloto, ele poderá solicitar dados que estão visíveis no painel atualmente ativo de um usuário no Terminal OpenBB.
Uma lista de todos os widgets atualmente visíveis no painel de um usuário é enviada ao seu copiloto na matriz de widgets
da carga útil da solicitação.
Para recuperar os dados de um widget, seu copiloto deve responder com um evento copilotFunctionCall
, especificando o UUID do widget:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"c276369e-e469-4689-b5fe-3f8c76f7c45a"}}
Após a emissão do evento copilotFunctionCall
, deve-se encerrar a conexão e aguardar uma nova solicitação de consulta do Terminal OpenBB.
Quando um evento copilotFunctionCall
for recebido, o Terminal OpenBB recuperará os dados e iniciará uma nova solicitação de consulta. Esta nova solicitação de consulta incluirá a chamada de função original, bem como o resultado da chamada de função no array 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>"
}
}
]
}
Observe que:
messages
.content
da mensagem ai
de chamada de função é um objeto JSON codificado em string literal do campo data
do evento copilotFunctionCall
(este é um mecanismo muito útil para contrabandear metadados adicionais relacionados à chamada de função, se seu copiloto precisar). Atualmente, a única chamada de função suportada pelo Terminal OpenBB é get_widget_data
, que recupera dados de um widget específico.
Seu copiloto personalizado recebe a seguinte solicitação do 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
}
}
]
}
Em seguida, você analisa a resposta e formata as mensagens em seu LLM (incluindo informações sobre quais widgets estão disponíveis). Vamos supor que seu copiloto determine que a consulta do usuário pode ser respondida usando o widget disponível e gere uma chamada de função para recuperar os dados.
Seu copiloto então responde com o seguinte SSE e fecha a conexão:
event: copilotFunctionCall
data: {"function":"get_widget_data","input_arguments":{"widget_uuid":"38181a68-9650-4940-84fb-a3f29c8869f3"}}
O Terminal OpenBB executará então a função especificada e fará uma nova solicitação de consulta ao seu 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
}
}
}
Em seguida, você analisa a resposta, processa os dados e formata as mensagens em seu LLM. Vamos supor que o LLM gere uma sequência de tokens para responder à consulta do usuário. Eles são então transmitidos de volta ao usuário usando o 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 seu copiloto personalizado ao Terminal OpenBB, você precisa configurar e servir um arquivo copilots.json
. Este arquivo define como seu copiloto personalizado se conecta ao front-end, incluindo quais recursos são suportados por seu copiloto personalizado e para onde as solicitações devem ser enviadas.
Aqui está um exemplo de configuração do 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"
}
}
}
Seu arquivo copilots.json
deve ser servido em <your-host>/copilots.json
, por exemplo, http://localhost:7777/copilots.json
.