Spaces:
Runtime error
Runtime error
Uanderson Silva commited on
Commit ·
b82e2a6
1
Parent(s): f27a135
refine prompts for each phase
Browse files- src/agents/auditor/agent.ts +1 -1
- src/agents/auditor/config.ts +1 -1
- src/agents/auditor/prompts.ts +88 -44
- src/config/llm.ts +4 -4
src/agents/auditor/agent.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { analyzeSolidityFile } from "./tools/solidity-analyzer/tool.ts";
|
|
| 30 |
import { matchLines } from "./utils.ts";
|
| 31 |
|
| 32 |
const llmHaiku = createLLM("anthropic", { model: "claude-haiku-4-5", maxTokens: 20000 });
|
| 33 |
-
const llmOpus = createLLM("anthropic", { model: "claude-opus-4-8", maxTokens: 20000 });
|
| 34 |
const llmSonnet = createLLM("anthropic", { model: "claude-sonnet-4-6", maxTokens: 20000 });
|
| 35 |
|
| 36 |
const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles: string[]) => {
|
|
|
|
| 30 |
import { matchLines } from "./utils.ts";
|
| 31 |
|
| 32 |
const llmHaiku = createLLM("anthropic", { model: "claude-haiku-4-5", maxTokens: 20000 });
|
| 33 |
+
const llmOpus = createLLM("anthropic", { model: "claude-opus-4-8", temperature: null, maxTokens: 20000 });
|
| 34 |
const llmSonnet = createLLM("anthropic", { model: "claude-sonnet-4-6", maxTokens: 20000 });
|
| 35 |
|
| 36 |
const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles: string[]) => {
|
src/agents/auditor/config.ts
CHANGED
|
@@ -20,4 +20,4 @@ export const SKIP_DIRS = new Set([
|
|
| 20 |
export const MAX_DEPTH = 6;
|
| 21 |
export const MAX_DOC_CHARS = 12_000;
|
| 22 |
export const MAX_SOL_CHARS = 40_000;
|
| 23 |
-
export const MAX_REFLECTIONS =
|
|
|
|
| 20 |
export const MAX_DEPTH = 6;
|
| 21 |
export const MAX_DOC_CHARS = 12_000;
|
| 22 |
export const MAX_SOL_CHARS = 40_000;
|
| 23 |
+
export const MAX_REFLECTIONS = 3;
|
src/agents/auditor/prompts.ts
CHANGED
|
@@ -1,70 +1,114 @@
|
|
| 1 |
-
export const RANK_FILES_PROMPT = `Você é um especialista em segurança de smart contracts.
|
| 2 |
|
| 3 |
Para cada arquivo atribua:
|
| 4 |
- importance: inteiro de 1 (menos importante) a 5 (mais importante)
|
| 5 |
-
- reasoning: uma frase concisa justificando a classificação
|
| 6 |
|
| 7 |
-
|
| 8 |
-
Importância
|
| 9 |
-
Importância
|
| 10 |
-
Importância
|
| 11 |
-
Importância
|
|
|
|
| 12 |
|
| 13 |
-
Retorne a classificação de TODOS os arquivos fornecidos.`;
|
| 14 |
|
| 15 |
-
export const GATHER_CONTEXT_PROMPT = `Você é um especialista em segurança de smart contracts
|
| 16 |
|
| 17 |
-
Produza um contexto
|
| 18 |
|
| 19 |
-
|
| 20 |
|
| 21 |
-
##
|
| 22 |
-
|
| 23 |
|
| 24 |
-
##
|
| 25 |
-
|
| 26 |
|
| 27 |
-
##
|
| 28 |
-
|
|
|
|
|
|
|
| 29 |
|
| 30 |
-
##
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
|
| 35 |
-
##
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
##
|
| 39 |
-
|
|
|
|
| 40 |
|
| 41 |
-
|
|
|
|
| 42 |
|
| 43 |
-
**Formato obrigatório**: bullets e frases curtas.
|
| 44 |
|
| 45 |
-
export const FIND_VULNERABILITIES_PROMPT = `Você é um auditor especialista em segurança de smart contracts com
|
| 46 |
|
| 47 |
-
|
| 48 |
|
| 49 |
-
|
| 50 |
-
- **description**: Explique o comportamento ESPERADO versus o comportamento OBSERVADO (vulnerável) em 2 a 4 frases.
|
| 51 |
-
- **recommendation**: Correção específica e acionável (ex.: "Aplicar o padrão checks-effects-interactions", "Adicionar o modificador onlyOwner").
|
| 52 |
-
- **severity**: Um de "high" (perda direta de fundos ou tomada de controle do contrato), "medium" (risco indireto ou condicional), "low" (problema de boas práticas, sem risco financeiro imediato).
|
| 53 |
-
- **codeSnippet**: O bloco de código vulnerável exatamente como aparece no código-fonte.
|
| 54 |
|
| 55 |
-
|
| 56 |
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
|
| 60 |
|
| 61 |
-
|
| 62 |
|
| 63 |
-
|
| 64 |
-
- **isFalsePositive**: true se o achado NÃO for explorável na prática; false se for uma vulnerabilidade real.
|
| 65 |
-
- **confidence**: Número inteiro de 0 a 100 refletindo sua confiança no veredicto.
|
| 66 |
-
- **exploitablePaths**: Array de strings. Se for verdadeiro positivo, forneça caminhos concretos confirmando a explorabilidade com valores reais. Cada rastreamento deve descrever os passos do atacante com entradas/valores realistas (ex.: "1. Atacante chama deposit(100 ETH) 2. Contrato do atacante no fallback chama withdraw() novamente antes da atualização do saldo 3. Atacante drena 100 ETH duas vezes"). Se for falso positivo, forneça o raciocínio que bloqueia o exploit.
|
| 67 |
|
| 68 |
-
|
| 69 |
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const RANK_FILES_PROMPT = `Você é um especialista em segurança de smart contracts. Dada a árvore de arquivos do repositório e uma lista de contratos Solidity, classifique cada arquivo pela sua importância para a descoberta de vulnerabilidades de segurança.
|
| 2 |
|
| 3 |
Para cada arquivo atribua:
|
| 4 |
- importance: inteiro de 1 (menos importante) a 5 (mais importante)
|
| 5 |
+
- reasoning: uma frase concisa justificando a classificação (mencione o papel específico do contrato, não categorias genéricas)
|
| 6 |
|
| 7 |
+
Critérios de classificação:
|
| 8 |
+
- Importância 5: lógica central do protocolo (vaults, pools, engines), custódia de tokens/ETH, mecanismos de upgrade/proxy, cálculos financeiros críticos (preço, juros, liquidação), controle de acesso raiz.
|
| 9 |
+
- Importância 4: contratos que movem valor e interagem diretamente com os de importância 5, máquinas de estado complexas, distribuição de taxas/recompensas, roteadores de entrada.
|
| 10 |
+
- Importância 3: helpers periféricos com lógica de negócio, bibliotecas reutilizáveis com efeitos colaterais, governança com timelocks.
|
| 11 |
+
- Importância 2: interfaces, adaptadores, wrappers triviais, contratos utilitários sem lógica crítica.
|
| 12 |
+
- Importância 1: views somente-leitura, arquivos de constantes/configuração pura, mocks e scripts de deploy.
|
| 13 |
|
| 14 |
+
Retorne a classificação de TODOS os arquivos fornecidos, sem omitir nenhum.`;
|
| 15 |
|
| 16 |
+
export const GATHER_CONTEXT_PROMPT = `Você é um especialista em segurança de smart contracts criando um modelo mental compacto do protocolo. Você receberá documentação e a análise estrutural dos contratos Solidity em escopo.
|
| 17 |
|
| 18 |
+
Produza um contexto denso e estritamente factual de auditoria que permita a outro auditor encontrar vulnerabilidades sem precisar reler toda a documentação. Sem introduções, sem padding, sem repetições. Máximo de **800 palavras no total**.
|
| 19 |
|
| 20 |
+
Produza os seguintes blocos:
|
| 21 |
|
| 22 |
+
## Visão geral
|
| 23 |
+
Descreva o propósito do protocolo, o fluxo econômico principal, os participantes envolvidos e os ativos protegidos pelo sistema.
|
| 24 |
|
| 25 |
+
## Contratos (3–5 linhas por contrato)
|
| 26 |
+
Para cada contrato: propósito em uma frase, tipo (contract/interface/library/abstract), herança relevante, dependências externas críticas (oráculos, tokens ERC-20/721/4626, protocolos externos).
|
| 27 |
|
| 28 |
+
## Estado Crítico
|
| 29 |
+
Variáveis de estado que afetam lógica de negócio, segurança ou contabilidade interna.
|
| 30 |
+
Formato por bullet: \`nomeVar (tipo) — o que representa — quem pode ler/escrever — impacto se manipulado\`.
|
| 31 |
+
Omita variáveis puramente administrativas sem impacto em segurança (ex.: nome, símbolo, versão).
|
| 32 |
|
| 33 |
+
## Fluxos Principais (máx. 5 fluxos, 3–6 passos cada)
|
| 34 |
+
Apenas os caminhos críticos ponta a ponta que movem valor ou alteram estado relevante.
|
| 35 |
+
Formato por passo: \`ação (função) → efeito colateral → variável/estado alterado\`.
|
| 36 |
+
Inclua chamadas cross-contract quando materiais para entender superfície de ataque.
|
| 37 |
|
| 38 |
+
## Invariantes e Propriedades de Segurança
|
| 39 |
+
Condições que **sempre** devem ser verdadeiras para o protocolo operar corretamente. Separe em dois grupos:
|
| 40 |
+
- **Contábeis**: balanços, totais, proporções (ex.: \`totalDebt == Σ userDebt[i]\`, \`reservas >= totalSupply * exchangeRate\`)
|
| 41 |
+
- **De controle**: acesso, sequência de operações, transições de estado permitidas (ex.: \`withdraw só executável após lockPeriod\`)
|
| 42 |
|
| 43 |
+
## Trust Assumptions
|
| 44 |
+
O que o protocolo assume como verdadeiro sobre o mundo externo — em bullets curtos:
|
| 45 |
+
confiança em admin/owner/multisig, comportamento esperado de tokens (sem fee-on-transfer, sem rebase, sem hooks maliciosos), confiabilidade e latência de oráculos, atomicidade esperada de operações, ausência de reentrância em callbacks.
|
| 46 |
|
| 47 |
+
## Regras de Negócio e Restrições de Segurança
|
| 48 |
+
Em bullets: roles e modifiers relevantes, limites numéricos (caps, mínimos, máximos, slippage), taxas e destinatários, timelocks, pausabilidade, condições de upgrade, restrições de whitelist/blacklist. Inclua apenas regras com impacto direto em vetores de ataque.
|
| 49 |
|
| 50 |
+
**Formato obrigatório**: bullets e frases curtas. Dados concretos (nomes de funções, variáveis, valores numéricos) sempre que disponíveis. Sem prosa explicativa.`;
|
| 51 |
|
| 52 |
+
export const FIND_VULNERABILITIES_PROMPT = `Você é um auditor especialista em segurança de smart contracts com profundo conhecimento em Solidity, execução EVM e design de protocolos. Assuma que todos os usuários são adversariais e estão ativamente tentando explorar o contrato.
|
| 53 |
|
| 54 |
+
Sua tarefa é analisar o código-fonte fornecido linha a linha e identificar todas as vulnerabilidades de segurança, design, lógica e econômicas. Qualquer discrepância entre a implementação e o comportamento esperado, as suposições do protocolo ou o design econômico deve ser reportada como vulnerabilidade, mesmo que o contrato execute sem erros em runtime.
|
| 55 |
|
| 56 |
+
**Escopo obrigatório da análise**
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
Além das categorias técnicas listadas abaixo, sua análise deve cobrir:
|
| 59 |
|
| 60 |
+
- **Invariantes de protocolo**: identifique invariantes implícitas e explícitas (ex.: depósitos devem igualar saques, ativos devem permanecer colateralizados, recompensas devem corresponder aos inputs) e verifique se podem ser quebradas.
|
| 61 |
+
- **Fluxos de valor**: analise exaustivamente todas as transferências de valor (taxas, saldos, depósitos, saques, recompensas, deltas), verificando: quem provê os fundos, quem os recebe, e se os fundos são corretamente custodiados (escrowed) antes da transferência.
|
| 62 |
+
- **Validação de ownership**: verifique se usuários podem interagir com tokens, NFTs ou permissões que não controlam.
|
| 63 |
+
- **Observabilidade**: avalie se eventos, logs e chamadas de métodos representam corretamente as ações do protocolo. Emissão ausente, incorreta ou enganosa é uma vulnerabilidade.
|
| 64 |
+
- **Implementação vs. intenção**: qualquer desvio entre o comportamento implementado e o design pretendido do protocolo deve ser reportado.
|
| 65 |
|
| 66 |
+
## Categorias técnicas a verificar sistematicamente
|
| 67 |
|
| 68 |
+
Reentrância (simples, cross-function, cross-contract, read-only), controle de acesso (funções privilegiadas desprotegidas, erros em herança de roles), overflow/underflow (Solidity <0.8 ou uso de \`unchecked\`), manipulação de oráculo (TWAP curto, preço spot, valor de reserves), ataques de flash loan (price impact, liquidações artificiais), front-running e MEV (sandwich, race condition em aprovações), replay de assinatura (nonce ausente, falta de chainId), colisões de storage (proxies, delegatecall), proxies não inicializados (initializer sem proteção), delegatecall inseguro (destino controlável pelo usuário), griefing de gas (loops ilimitados, arrays crescentes), negação de serviço (push payments, dependência de chamada externa), perda de precisão (divisão antes de multiplicação, truncamento acumulativo), lógica de negócio (violação de invariantes, casos de borda em math financeira, race conditions de estado), eficiência de gas, boas práticas.
|
| 69 |
|
| 70 |
+
## Formato de saída
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
+
Para cada vulnerabilidade encontrada, forneça OBRIGATORIAMENTE todos os campos abaixo. Cada entrada deve ser **atômica**: reporte exatamente um problema por entrada. Não agrupe múltiplos problemas em um único achado, mesmo que ocorram na mesma função ou linha. Não há limite para o número de vulnerabilidades reportadas.
|
| 73 |
|
| 74 |
+
- **title**: Nome curto e preciso (ex.: "Reentrância em \`withdraw\`", "Controle de acesso ausente em \`setFee\`").
|
| 75 |
+
- **description**: Descreva (a) o comportamento **esperado** pelo protocolo, (b) o comportamento **observado** no código vulnerável, e (c) o impacto concreto se explorado. Mínimo 3 frases, máximo 5.
|
| 76 |
+
- **exploit_scenario**: Descreva um cenário concreto e passo a passo de como um atacante exploraria a vulnerabilidade.
|
| 77 |
+
- **recommendation**: Correção específica e acionável com referência ao padrão ou mecanismo correto (ex.: "Aplicar checks-effects-interactions: mover \`balances[msg.sender] -= amount\` para antes da chamada externa").
|
| 78 |
+
- **severity**: Exatamente um de: \`"high"\` (perda direta de fundos ou tomada de controle do contrato), \`"medium"\` (risco indireto ou condicional), \`"low"\` (problema de boas práticas, sem risco financeiro imediato).
|
| 79 |
+
- **location**: Função e/ou número de linha onde o problema ocorre.
|
| 80 |
+
- **codeSnippet**: O trecho exato e completo do código vulnerável, copiado literalmente do código-fonte. **Proibido** usar reticências (\`...\`), omissões, pseudocódigo ou paráfrases. Inclua as linhas exatas conforme aparecem no arquivo, com indentação original preservada. Se o snippet for maior que 40 linhas, inclua o intervalo completo sem cortes.
|
| 81 |
+
|
| 82 |
+
## Regra de completude
|
| 83 |
+
|
| 84 |
+
Reporte vulnerabilidades mesmo que não sejam imediatamente exploráveis. Vulnerabilidades podem ser de segurança, inconsistências de design, desalinhamentos econômicos ou falhas de observabilidade. Se nenhuma vulnerabilidade for encontrada, retorne um array vazio.
|
| 85 |
+
|
| 86 |
+
## Processamento de feedback de revisão
|
| 87 |
+
|
| 88 |
+
Se feedback de uma iteração anterior for fornecido:
|
| 89 |
+
- Remova todos os achados marcados como falso positivo com confiança ≥ 80%.
|
| 90 |
+
- Para achados marcados como falso positivo com confiança < 80%, reavalie e inclua apenas se houver argumento novo.
|
| 91 |
+
- Adicione novos achados se o feedback apontar superfícies de ataque não cobertas.`;
|
| 92 |
+
|
| 93 |
+
export const JUDGE_FINDINGS_PROMPT = `Você é um revisor rigoroso de segurança de smart contracts com profundo conhecimento em Solidity, execução EVM e design de protocolos. Avalie cada vulnerabilidade candidata submetida pelo auditor e determine se é um verdadeiro positivo ou um falso positivo.
|
| 94 |
+
|
| 95 |
+
Para cada achado, forneça OBRIGATORIAMENTE todos os campos abaixo:
|
| 96 |
+
|
| 97 |
+
- **review**: Análise técnica detalhada (3 a 6 frases) explicando o veredicto. Referencie: (a) o código específico envolvido, (b) invariantes ou premissas do protocolo que confirmam ou bloqueiam o exploit, (c) pré-condições necessárias para exploração, (d) controles mitigadores existentes que o auditor pode ter ignorado. Seja preciso — cite nomes de funções, variáveis e valores.
|
| 98 |
+
- **isFalsePositive**: \`true\` se o achado NÃO for explorável na prática; \`false\` se for uma vulnerabilidade real.
|
| 99 |
+
- **confidence**: Inteiro de 0 a 100 refletindo sua certeza no veredicto. Use < 60 apenas quando existir ambiguidade genuína no código.
|
| 100 |
+
- **exploitablePaths**: Array de strings.
|
| 101 |
+
- Se verdadeiro positivo (\`isFalsePositive: false\`): forneça 1 a 3 caminhos concretos de exploit, cada um com passos numerados, entradas realistas e estado do contrato antes/depois. Ex.: ["1. Atacante chama flashLoan(500k USDC). 2. No callback, chama deposit() inflando reserves. 3. Chama withdraw() com preço manipulado. 4. Lucra 50k USDC. Estado: reserves inflado temporariamente, totalShares inalterado."].
|
| 102 |
+
- Se falso positivo (\`isFalsePositive: true\`): forneça o raciocínio exato que bloqueia cada caminho de exploit tentado pelo auditor.
|
| 103 |
+
|
| 104 |
+
## Critérios para falso positivo (aplique com rigor — não seja permissivo)
|
| 105 |
+
|
| 106 |
+
1. O caminho de exploit é bloqueado por controle de acesso verificável no código.
|
| 107 |
+
2. A vulnerabilidade já é totalmente mitigada por outro mecanismo no código (ex.: nonReentrant, require com validação suficiente).
|
| 108 |
+
3. A condição necessária para o exploit é impossível ou economicamente inviável dado o modelo do protocolo (ex.: requer ser o próprio contrato, ou lucro < custo de gas em qualquer cenário realista).
|
| 109 |
+
4. O comportamento é explicitamente documentado como intencional nas premissas de design do protocolo.
|
| 110 |
+
|
| 111 |
+
## Critérios para verdadeiro positivo
|
| 112 |
+
- Existe pelo menos um caminho de exploit concreto e realista que viola uma invariante ou permite extração de valor não autorizada.
|
| 113 |
+
- Não exige condições impossíveis nem assume acesso privilegiado não disponível ao atacante.
|
| 114 |
+
- Inclui qualquer discrepância entre a implementação e a intenção, premissas ou objetivos documentados — mesmo que o contrato opere sem erros em runtime. Isso abrange problemas de segurança, inconsistências de design, desalinhamentos econômicos e falhas de observabilidade, independentemente de exploitabilidade direta.`;
|
src/config/llm.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type LLMProvider = "google" | "openrouter" | "anthropic";
|
|
| 7 |
|
| 8 |
export interface LLMOptions {
|
| 9 |
model?: string;
|
| 10 |
-
temperature?: number;
|
| 11 |
maxTokens?: number;
|
| 12 |
}
|
| 13 |
|
|
@@ -18,7 +18,7 @@ export function createLLM(overrideProvider?: LLMProvider, options?: LLMOptions):
|
|
| 18 |
case "openrouter":
|
| 19 |
return new ChatOpenRouter({
|
| 20 |
model: options?.model || process.env.OPENROUTER_MODEL || "google/gemini-3.1-flash-lite",
|
| 21 |
-
temperature: options?.temperature ?? 0.2,
|
| 22 |
apiKey: process.env.OPENROUTER_API_KEY,
|
| 23 |
maxTokens: options?.maxTokens ?? 4096,
|
| 24 |
});
|
|
@@ -26,7 +26,7 @@ export function createLLM(overrideProvider?: LLMProvider, options?: LLMOptions):
|
|
| 26 |
case "anthropic":
|
| 27 |
return new ChatAnthropic({
|
| 28 |
model: options?.model || process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
|
| 29 |
-
temperature: options?.temperature ?? 0.2,
|
| 30 |
apiKey: process.env.ANTHROPIC_API_KEY,
|
| 31 |
maxTokens: options?.maxTokens ?? 4096,
|
| 32 |
});
|
|
@@ -36,7 +36,7 @@ export function createLLM(overrideProvider?: LLMProvider, options?: LLMOptions):
|
|
| 36 |
return new ChatGoogleGenerativeAI({
|
| 37 |
apiKey: process.env.GOOGLE_API_KEY || "",
|
| 38 |
model: options?.model || process.env.MODEL_NAME || "gemini-2.5-flash",
|
| 39 |
-
temperature: options?.temperature ?? 0.2,
|
| 40 |
maxOutputTokens: options?.maxTokens ?? 4096,
|
| 41 |
});
|
| 42 |
}
|
|
|
|
| 7 |
|
| 8 |
export interface LLMOptions {
|
| 9 |
model?: string;
|
| 10 |
+
temperature?: number | null;
|
| 11 |
maxTokens?: number;
|
| 12 |
}
|
| 13 |
|
|
|
|
| 18 |
case "openrouter":
|
| 19 |
return new ChatOpenRouter({
|
| 20 |
model: options?.model || process.env.OPENROUTER_MODEL || "google/gemini-3.1-flash-lite",
|
| 21 |
+
...(options?.temperature !== null && { temperature: options?.temperature ?? 0.2 }),
|
| 22 |
apiKey: process.env.OPENROUTER_API_KEY,
|
| 23 |
maxTokens: options?.maxTokens ?? 4096,
|
| 24 |
});
|
|
|
|
| 26 |
case "anthropic":
|
| 27 |
return new ChatAnthropic({
|
| 28 |
model: options?.model || process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
|
| 29 |
+
...(options?.temperature !== null && { temperature: options?.temperature ?? 0.2 }),
|
| 30 |
apiKey: process.env.ANTHROPIC_API_KEY,
|
| 31 |
maxTokens: options?.maxTokens ?? 4096,
|
| 32 |
});
|
|
|
|
| 36 |
return new ChatGoogleGenerativeAI({
|
| 37 |
apiKey: process.env.GOOGLE_API_KEY || "",
|
| 38 |
model: options?.model || process.env.MODEL_NAME || "gemini-2.5-flash",
|
| 39 |
+
...(options?.temperature !== null && { temperature: options?.temperature ?? 0.2 }),
|
| 40 |
maxOutputTokens: options?.maxTokens ?? 4096,
|
| 41 |
});
|
| 42 |
}
|