Sistema De Tools
O MangoStudio suporta tool calling agnóstico a provedor durante turnos de chat. Modelos podem chamar tools, o sistema as executa e os resultados são devolvidos ao modelo em loop.
Arquitetura
Camada HTTP (tool-settings-routes.ts)
│
Camada de aplicação (tool-settings-service.ts)
│── usa ──→ Registry (registry.ts)
│── usa ──→ Settings Policy (settings-policy.ts)
│── usa ──→ Repository (tool-settings-repository.ts)
│
DB: user_tool_settings
│
Camada de tools (types.ts + registry.ts)
│── registra ──→ Built-ins (generate-image.ts, get-current-datetime.ts)
│
Camada do provedor (tool-mapper.ts) ──→ formatos wire específicos
Ciclo De Vida Das Tools
- Registro — Tools se auto-registram em tempo de import via
registerTool(). Tools built-in fazem isso no load do módulo. - Resolução de settings — No momento do chat,
getEnabledToolRuntime()carrega tool settings do usuário no DB, faz merge com defaults e produzToolDefinition[]habilitadas. - Mapeamento do formato wire —
tool-mapper.tsconverteToolDefinition[]para o formato específico do provedor, como function tools da OpenAI ou function declarations do Gemini. - Modelo chama a tool — O provedor faz streaming de eventos
tool_call_started,tool_call_arguments_deltaetool_call_completed. - Execução —
executeTool()faz lookup da tool, verifica se ela está habilitada para o usuário, aplica merge de settings e executa o executor. - Devolução do resultado — Tool results são serializados e enviados ao modelo na próxima iteração do loop.
- Nova iteração — Os passos 4–6 se repetem até que o modelo produza uma resposta em texto ou o limite máximo de iterações seja atingido.
Tipos Centrais
ToolDefinition
interface ToolDefinition {
name: string;
description: string;
parameters: JsonSchema; // JSON Schema dos argumentos da tool
}
RegisteredTool
interface RegisteredTool {
definition: ToolDefinition;
buildDefinition?: (settings: EffectiveToolSettings) => ToolDefinition;
settings: ToolSettingsMetadata;
execute: (args: unknown, context: ToolContext) => Promise<unknown>;
}
ToolSettingsMetadata
interface ToolSettingsMetadata {
title: string;
description: string;
category: 'system' | 'image' | 'interaction';
enabledByDefault: boolean;
canDisable: boolean;
defaultParameters: Record<string, unknown>;
parameterDescriptors: ToolParameterDescriptor[];
}
Tools Built-in
generate_image
Cria uma ou mais imagens via modelos de geração de imagem durante um turno de chat de texto.
- Nome da tool:
generate_image - Categoria:
image - Parâmetros:
prompt(obrigatório),count(1–4),quality,model - Schema dependente de settings:
maxImagesPerCallé limitado dinamicamente com base nas configurações do usuário. - Execução: Planeja imagens com
createGenerateImageToolPlan(), produz resultados por imagem via streaming e resume tudo em um único resultado.
get_current_datetime
Retorna a data e hora atuais em um fuso horário e locale solicitados.
- Nome da tool:
get_current_datetime - Categoria:
system - Parâmetros:
timezone(IANA, ex.America/Sao_Paulo),locale(BCP 47, ex.pt-BR) - Execução: Valida o timezone, formata via
Intl.DateTimeFormate retorna UTC ISO + datetime localizado + offset.
read_file
Lê o conteúdo de um arquivo de texto do disco.
- Nome da tool:
read_file - Categoria:
system - Parâmetros:
path(obrigatório, absoluto ou começando com~) - Settings:
allowedPaths,deniedPaths(listas de caminhos; aplicadas porresolveAndValidatePath) - Execução: Lê o arquivo com
Bun.file().text()e retorna{ content, path, size }.
list_directory
Lista arquivos e diretórios em um caminho.
- Nome da tool:
list_directory - Categoria:
system - Parâmetros:
path(obrigatório, absoluto ou começando com~) - Settings:
allowedPaths,deniedPaths - Execução: Chama
readdir(path, { withFileTypes: true })e retorna{ path, entries: { name, type }[] }.
glob
Encontra caminhos do filesystem que correspondem a um padrão glob, avaliados por Bun.Glob.
- Nome da tool:
glob - Categoria:
system - Parâmetros:
pattern(obrigatório, suporta*,**,?,[],{a,b},!),cwd(diretório base opcional; padrãoprocess.cwd()) - Settings:
allowedPaths,deniedPaths,maxResults(1–5.000; padrão 200),includeDotfiles(padrãofalse),absolute(padrãofalse) - Execução: Itera os matches com
new Bun.Glob(pattern).scan({ cwd, dot, absolute, onlyFiles: false }), para ao atingir o limite e sinalizatruncated.
grep
Pesquisa nos arquivos por linhas que correspondam a uma expressão regular.
- Nome da tool:
grep - Categoria:
system - Parâmetros:
pattern(regex obrigatória),path(arquivo ou diretório obrigatório),glob(filtro opcional para buscas em diretório),caseInsensitive - Settings:
allowedPaths,deniedPaths,maxResults(1–5.000; padrão 100),maxMatchesPerFile(padrão 20),maxFileSizeBytes(padrão 1 MB),includeDotfiles - Segurança: Arquivos com byte nulo nos primeiros 1 KB são tratados como binários e ignorados; arquivos acima de
maxFileSizeBytestambém são pulados. A regex é compilada comnew RegExpe rejeitada viaGrepPatternErrorquando inválida. - Execução: Quando
pathé um diretório, percorre-o comBun.Glob(filtrado peloglobopcional); para cada candidato lê comBun.file().text(), divide por linha e registra matches{ file, line, text }.
bash / zsh / powershell
Executam um comando de shell e retornam stdout, stderr, código de saída e tempo capturados. As três tools compartilham uma única implementação (buildShellTool) e diferem apenas pelo interpretador.
- Nomes das tools:
bash,zsh,powershell - Categoria:
system - Parâmetros:
command(obrigatório),cwd(diretório de trabalho opcional;~é expandido) - Settings:
timeoutMs(1s–30s, padrão 15s),maxOutputBytes(1KB–1MB por stream, padrão 100KB) - Disponibilidade: Registradas no import apenas quando o interpretador existe —
bash/zshviaBun.which,powershellsomente no Windows (pwshe depoispowershell). Shells indisponíveis nunca são oferecidos aos modelos. - Segurança: Desabilitadas por padrão (
enabledByDefault: false); exigem ativação explícita. O processo é encerrado comSIGKILLapóstimeoutMs, e a saída por stream é limitada amaxOutputBytes(sinalizado portruncated). - Execução:
runShellCommand()inicia o interpretador comBun.spawn(bash -c/zsh -c/powershell -NoProfile -NonInteractive -Command), lê ambos os streams dentro do limite de bytes e retorna umShellCommandResultestruturado.
Settings Policy
A settings policy em settings-policy.ts oferece funções puras para:
| Função | Finalidade |
|---|---|
getDefaultToolSettings(tool) |
Retorna defaults a partir dos metadados da tool |
mergeToolSettings(tool, saved?, updates?) |
Faz merge em três etapas: defaults < saved < overrides |
normalizeToolParameters(tool, params) |
Valida nomes, tipos, min/max e valores permitidos |
getToolDefinitionsForTools(tools, settings?) |
Filtra tools habilitadas e produz definitions |
A normalização de parâmetros lança ToolParameterError com mensagem descritiva quando valores são inválidos. executeTool() captura isso via getSafeEffectiveToolSettings() e cai para defaults para evitar que settings corrompidos quebrem a execução da tool.
API De Tool Settings
GET /api/settings/tools
Retorna todas as tools registradas com seus settings efetivos para o usuário atual.
Resposta: ToolSettingsListResponse
{
tools: ToolSettingsDescriptor[];
}
PUT /api/settings/tools/:toolName
Atualiza os settings de uma tool, incluindo estado enabled e parâmetros.
Request: UpdateToolSettingsBody
{
enabled?: boolean;
parameters?: Record<string, unknown>;
}
Retorna 422 com ToolSettingsError se os parâmetros forem inválidos ou se a tool não puder ser desabilitada.
Tool Mapper
tool-mapper.ts converte ToolDefinition internas para formatos wire específicos:
| Provedor | Mapper | Formato |
|---|---|---|
| OpenAI Responses | toolDefsToResponsesAPI() |
{ type: 'function', name, description, parameters, strict } |
| Gemini Interactions | toolDefsToGeminiInteractions() |
{ name, description, parameters } |
| OpenAI-compatible | toolDefsToChatCompletions() |
ChatCompletionTool[] |
A API OpenAI Responses aplica strict: true quando o schema satisfaz os requisitos do strict mode, como type: object, additionalProperties: false, todas as propriedades obrigatórias e ausência de oneOf, anyOf, allOf, not e $ref.
Adicionando Uma Nova Tool
- Crie o arquivo da tool em
apps/api/src/services/tools/builtin/. - Defina
ToolDefinition,ToolSettingsMetadatae a funçãoexecute. - Chame
registerTool()para auto-registro em tempo de import. - Importe a tool em
apps/api/src/services/tools/index.tspara disparar o registro. - Se a tool precisar de comportamento dependente de settings, forneça um callback
buildDefinition. - Adicione schemas TypeBox de request/response nos contratos compartilhados se a tool tiver sua própria superfície de API.
- Escreva testes unitários para o executor da tool e para o merge de settings.
Exemplo Mínimo
import { registerTool } from '../registry';
import type { RegisteredTool, ToolContext } from '../types';
const MY_TOOL: RegisteredTool = {
definition: {
name: 'my_tool',
description: 'Does something useful.',
parameters: {
type: 'object',
properties: {
input: { type: 'string', description: 'Input value' },
},
required: ['input'],
},
},
settings: {
title: 'My Tool',
description: 'A custom tool for specific tasks.',
category: 'interaction',
enabledByDefault: true,
canDisable: true,
defaultParameters: {},
parameterDescriptors: [],
},
execute: async (args, context) => {
const { input } = args as { input: string };
return { result: `Processed: ${input}` };
},
};
registerTool(MY_TOOL);