0. Setup del proyecto
Inicializa un proyecto Bun + TypeScript, instala QVAC y fija las dev tools que vas a usar el resto del walkthrough.
Antes de que exista cualquier archivo fuente, necesitas un proyecto
Bun + TypeScript funcionando. Este paso toma alrededor de un minuto
y produce la forma dentro de la cual van a correr después
src/cli.ts y src/server.ts.
Prerrequisitos
- Bun
>=1.3.0(instalación) - Node
>=22.17.0(usado por TypeScript y algunos módulosnode:)
Sin key de OpenAI. Sin cuenta de Pinecone. Sin GPU. Todo corre localmente a través de QVAC.
1. Crea el proyecto
mkdir locallens
cd locallens
bun init -ybun init -y escribe un package.json mínimo, un tsconfig.json,
un .gitignore y un index.ts placeholder. Borra index.ts — los
puntos de entrada reales (src/cli.ts, src/server.ts) vienen
después.
2. Instala dependencias
bun add @qvac/sdk
bun add -d typescript @types/bun @biomejs/biomePara qué es cada una:
| Paquete | Por qué |
|---|---|
@qvac/sdk | El loop de IA local: carga de modelos, RAG, completion. La única dependencia de runtime que LocalLens tiene. |
typescript | Tipos estrictos para los ocho archivos que estás por escribir. |
@types/bun | Definiciones de tipos para Bun.argv, Bun.serve, Bun.file, import.meta.dir. |
@biomejs/biome | Una sola herramienta para linting y formato. Reemplaza ESLint + Prettier. |
Esa es toda la superficie de dependencias. Sin base de datos vectorial, sin librería de embeddings, sin chunker.
3. Fija las versiones del runtime
Abre el package.json recién creado y pon type: "module" más el
campo engines, así cualquiera que clone el repo tiene el mismo
runtime:
{
"name": "locallens",
"version": "0.1.0",
"type": "module",
"private": true,
"license": "MIT",
"engines": {
"bun": ">=1.3.0",
"node": ">=22.17.0"
}
}4. Agrega los scripts que vas a usar
Agrega esto a package.json. Cada uno queda conectado por un paso
posterior. Por ahora solo necesitan 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 es el comando compuesto que usa CI. Corre lint →
tipos → tests → build, en ese orden, y sale con código distinto de
cero si algún paso falla.
5. Aprieta tsconfig.json
bun init produce un default razonable. El walkthrough quiere
algunos extras: strict mode, tipos de Bun y la habilidad de
importar extensiones .ts explícitamente. Reemplaza el archivo
generado con esto:
{
"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"]
}Dos flags que vale la pena marcar:
allowImportingTsExtensions: truete deja escribirimport "./domain.ts"en archivos fuente. Bun los corre directamente; la extensión explícita mantiene atsccontento.verbatimModuleSyntax: truerequiereimport typepara imports solo de tipos. El código del walkthrough está escrito así. Este flag lo impone.
6. Configura Biome
Biome maneja linting y formato. Un solo archivo 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"
}
}
}La exclusión !.locallens importa una vez que la app está corriendo.
Esa carpeta es donde viven el store JSON y los datos del cerebro, y
Biome no debería intentar formatearla.
7. Configura QVAC
QVAC lee su config de runtime desde qvac.config.json en la raíz
del repo. Créalo ahora para que el gateway tenga algo contra qué
cargar:
{
"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
}
}
}
}Algunas decisiones que vale la pena conocer:
preload: false— los modelos cargan en el primer uso, no en el boot. Mantienebun run devágil.ctx_size: 4096— la ventana de contexto del modelo de chat. Si subes el top-K en recuperación más adelante, mantén esto en la mira.temp: 0.2— temperatura baja para respuestas fundamentadas en citas. El modelo debe reformular la fuente, no inventar.
8. Haz lugar para el código fuente
mkdir src tests examplesCada paso del walkthrough de acá en adelante escribe un archivo en
src/. tests/ va a tener los dos tests de Bun que vas a agregar
para rag.ts y files.ts. examples/sample-brain/ es la carpeta
demo a la que apunta la CLI.
Dónde estás
El esqueleto del proyecto está listo: package.json, tsconfig.json,
biome.json, qvac.config.json, y tres carpetas vacías (src/,
tests/, examples/). No corras bun run typecheck todavía — con
include: ["src/**/*.ts", "tests/**/*.ts"] y nada dentro de ninguna
de las dos carpetas, TypeScript termina con:
error TS18003: No inputs were found in config file 'tsconfig.json'.Eso es lo esperado en este punto. El primer archivo fuente aterriza
en el siguiente paso. En cuanto src/domain.ts exista,
bun run typecheck se pone en verde y se queda así por el resto del
walkthrough.
Lo que `bun init` no te da
bun init -y es intencionalmente mínimo. No agrega Biome, QVAC,
flags estrictos de TypeScript, ni el set de scripts de arriba.
Trata esta página como la capa opinada que le pones encima a
bun init. Todo de acá en adelante asume que el proyecto se ve
como el resultado de estos ocho pasos.
Lo que sigue: tipos de dominio y AppError,
el primer archivo fuente real.