0. Setup do projeto
Inicialize um projeto Bun + TypeScript, instale o QVAC e fixe as dev tools que você vai usar pelo resto do walkthrough.
Antes de qualquer arquivo fonte existir, você precisa de um projeto Bun + TypeScript
funcionando. Esse passo leva um minuto e produz a forma que
o src/cli.ts e o src/server.ts futuros vão rodar por dentro.
Pré-requisitos
- Bun
>=1.3.0(install) - Node
>=22.17.0(usado pelo TypeScript e alguns módulosnode:)
Sem chave da OpenAI. Sem conta no Pinecone. Sem GPU. Tudo roda localmente através do QVAC.
1. Criar o projeto
mkdir locallens
cd locallens
bun init -ybun init -y escreve um package.json, tsconfig.json,
.gitignore mínimos, e um index.ts placeholder. Apaga o index.ts — os
pontos de entrada reais (src/cli.ts, src/server.ts) vêm depois.
2. Instalar dependências
bun add @qvac/sdk
bun add -d typescript @types/bun @biomejs/biomePara que serve cada um:
| Pacote | Porquê |
|---|---|
@qvac/sdk | O loop de IA local: carregamento de modelo, RAG, completion. A única dependência de runtime que o LocalLens tem. |
typescript | Tipos estritos para os oito arquivos que você vai escrever. |
@types/bun | Definições de tipo para Bun.argv, Bun.serve, Bun.file, import.meta.dir. |
@biomejs/biome | Uma ferramenta para lint e format. Substitui ESLint + Prettier. |
Essa é a superfície de dependência inteira. Sem banco vetorial, sem biblioteca de embedding, sem chunker.
3. Fixar as versões de runtime
Abre o package.json recém-criado e coloca type: "module"
mais o campo engines, para que qualquer um clonando o repositório pegue o mesmo
runtime:
{
"name": "locallens",
"version": "0.1.0",
"type": "module",
"private": true,
"license": "MIT",
"engines": {
"bun": ">=1.3.0",
"node": ">=22.17.0"
}
}4. Adicionar os scripts que você vai usar
Adiciona estes ao package.json. Cada um é conectado por um passo
seguinte. Por enquanto só precisam existir:
{
"scripts": {
"cli": "bun run src/cli.ts",
"dev": "bun run src/server.ts",
"start": "bun run src/server.ts",
"build": "bun build src/server.ts --target=bun --outdir=dist",
"test": "bun test",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"format": "biome format --write .",
"check": "bun run lint && bun run typecheck && bun run test && bun run build"
}
}bun run check é o comando composto que o CI usa. Roda lint →
types → tests → build, nessa ordem, e sai com código não-zero se qualquer
passo falhar.
5. Apertar o tsconfig.json
bun init produz um default razoável. O walkthrough quer
uns extras: modo estrito, tipos do Bun, e a capacidade de importar com
extensões .ts explícitas. Substitui o arquivo gerado por este:
{
"compilerOptions": {
"target": "ES2023",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["bun"],
"allowImportingTsExtensions": true,
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"verbatimModuleSyntax": true
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}Duas flags que vale apontar:
allowImportingTsExtensions: truedeixa você escreverimport "./domain.ts"em arquivos fonte. O Bun roda direto; a extensão explícita mantém otscfeliz.verbatimModuleSyntax: trueexigeimport typepara imports só de tipo. O código do walkthrough é escrito assim. Essa flag impõe isso.
6. Configurar o Biome
O Biome cuida de lint e format. Um arquivo de config:
{
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
"files": {
"includes": ["**", "!dist", "!node_modules", "!.locallens"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}A exclusão !.locallens importa quando o app está rodando. Essa
pasta é onde o JSON store e os dados dos brains vivem, e o Biome
não deve tentar formatar.
7. Configurar o QVAC
O QVAC lê o config de runtime de qvac.config.json na raiz do
repositório. Cria agora para o gateway ter o que carregar:
{
"loggerLevel": "info",
"loggerConsoleOutput": true,
"httpDownloadConcurrency": 3,
"serve": {
"models": {
"locallens-chat": {
"model": "QWEN3_1_7B_INST_Q4",
"default": true,
"preload": false,
"config": {
"ctx_size": 4096,
"temp": 0.2,
"top_p": 0.9
}
},
"locallens-embed": {
"model": "GTE_LARGE_FP16",
"default": true,
"preload": false
}
}
}
}Algumas escolhas que vale conhecer:
preload: false— modelos carregam no primeiro uso, não no boot. Mantém obun run devresponsivo.ctx_size: 4096— a janela de contexto do modelo de chat. Se você aumentar o top-K na recuperação depois, deixa isso à vista.temp: 0.2— temperatura baixa para respostas embasadas em citações. O modelo deve reformular a fonte, não inventar.
8. Fazer um lugar para o código
mkdir src tests examplesTodo passo do walkthrough daqui em diante escreve um arquivo em src/.
tests/ vai guardar os dois testes Bun que você vai adicionar para rag.ts e
files.ts. examples/sample-brain/ é a pasta demo que a CLI
aponta.
Onde você está
O esqueleto do projeto está pronto: package.json, tsconfig.json,
biome.json, qvac.config.json, e três pastas vazias (src/,
tests/, examples/). Não rode bun run typecheck ainda — com
include: ["src/**/*.ts", "tests/**/*.ts"] e nada dentro de nenhuma
das duas pastas, o TypeScript sai com:
error TS18003: No inputs were found in config file 'tsconfig.json'.Isso é esperado nesse momento. O primeiro arquivo fonte aparece no
próximo passo. Assim que src/domain.ts existir, bun run typecheck
fica verde e continua verde pelo resto do walkthrough.
O que o `bun init` não te dá
bun init -y é minimal de propósito. Não adiciona Biome,
QVAC, flags estritas de TypeScript, ou o conjunto de scripts acima. Trata
essa página como a camada opinada que você parafusa em cima do bun init.
Tudo daqui em diante assume que o projeto se parece com o resultado
desses oito passos.
A seguir: tipos de domínio e AppError,
o primeiro arquivo fonte de verdade.