Synsema docsENES

Config de proveedor

El proveedor lo selecciona y configura el runtime, no el programa — el .syn nunca nombra un host ni una clave, así que no puede redirigir la llamada ni filtrar la clave. Cada knob resuelve environ del proceso > .env > default.

.env (gitignoreado)

SYNSEMA_LLM_PROVIDER=deepseek
DEEPSEEK_API_KEY=sk-...

La clave llega al runtime sin entrar al environ del proceso — ningún proceso hijo ni programa puede leerla (el programa necesitaría require secret, y aun así la ve redactada). No hace falta export/source.

Knobs

Env var / entrada .envPropósitoDefault
ANTHROPIC_API_KEY / OPENAI_API_KEY / MINIMAX_API_KEY / DEEPSEEK_API_KEYAPI key; su presencia auto-selecciona el proveedor— (offline si falta)
SYNSEMA_LLM_PROVIDERForzar anthropic / openai / minimax / deepseekauto
SYNSEMA_LLM_MODELId de modeloclaude-sonnet-4-6 / gpt-4o / MiniMax-M3 / deepseek-chat
SYNSEMA_LLM_MAX_TOKENSTope de salida4096
SYNSEMA_LLM_BASE_URLBase del endpoint (apuntar a un server local/compatible)oficial
SYNSEMA_LLM_TIMEOUTTimeout HTTP en segundos de los providers de red. Con el transporte streaming (default) mide silencio entre bytes — cada chunk lo renueva — así que una generación de minutos fluye y un host muerto sigue fallando rápido. Inválido/≤0 → default60
SYNSEMA_LLM_HTTP_STREAMTransporte SSE interno de los providers de red (las ops del lenguaje siguen devolviendo texto completo; llm_stream emite chunks reales). 0/false → camino no-stream clásico, escape para proxies raros1 (on)
SYNSEMA_LLM_BUDGETPresupuesto de tokens LLM por proceso (input + output, todas las ops). Al tope, toda op degrada al marker [llm budget exceeded: used N of M tokens] — sin error, sin llamada de red, un aviso por stderr. El consumo se lee con llm_usage() (ver Primitivas LLM). Inválido/0 → sin presupuesto, con aviso— (ilimitado)

También podés forzar el proveedor por corrida: synsema run app.syn --provider anthropic (flag > env > .env).

El timeout ya no limita las generaciones largas. Los providers de red piden la respuesta a la API en modo streaming y la rearman internamente, así que el read-timeout detecta *conexiones muertas* (60s de silencio real) en vez de limitar la duración total de la generación. Verificado en vivo: una generación de 177s / 68 KB (MiniMax-M3, modelo razonador) completa donde la ventana fija de 60s la mataba. Con SYNSEMA_LLM_HTTP_STREAM=0 el timeout limita la llamada completa, como antes.

Local / on-prem (100% privado)

Cualquier server compatible con OpenAI (Ollama, LM Studio, vLLM):

SYNSEMA_LLM_PROVIDER=openai
SYNSEMA_LLM_BASE_URL=http://localhost:11434/v1   # Ollama
SYNSEMA_LLM_MODEL=llama3.1
OPENAI_API_KEY=ollama                            # cualquier valor no vacío

Proveedor local embebido (local) — GGUF dentro del proceso, cero red

Con un binario compilado con la feature llm-local, el runtime corre un GGUF cuantizado dentro del proceso de Synsema (candle, CPU): sin server, sin API key, sin socket — el único proveedor que funciona bajo un deny net total. Siempre explícito, jamás auto-seleccionado:

SYNSEMA_LLM_PROVIDER=local
SYNSEMA_LLM_MODEL=/models/qwen2.5-0.5b-instruct-q4_k_m.gguf   # ruta al .gguf (obligatoria)
KnobPropósitoDefault
SYNSEMA_LLM_CTXVentana de contexto (capada al límite del propio GGUF)4096
SYNSEMA_LLM_THREADSThreads de CPU para la inferenciatodos los cores
SYNSEMA_LLM_TEMPERATURE0 = greedy/determinista; >0 = muestreo (seed fija)0
SYNSEMA_LLM_MAX_CONCURRENTInstancias máximas del modelo; 1 serializa llamadas concurrentes bajo serve1
SYNSEMA_LLM_STREAM_BUFFERChunks en vuelo entre la generación y la emisión de llm_stream32

SYNSEMA_LLM_MAX_TOKENS aplica como siempre. Arquitecturas soportadas: llama, qwen2, qwen3 — es el string general.architecture del header del GGUF, no la marca del modelo; cualquier otra falla con un [local error: …] claro. Muchas familias populares se convierten a GGUF declarando una de esas tres, así que la cobertura real es más amplia de lo que sugieren los tres nombres. Familias verificadas en vivo (sonda = cargar + pregunta + respuesta coherente + corte limpio):

Modelo (GGUF sondeado)declaraVerificado
Qwen2.5 Instruct 0.5B/3Bqwen2
Qwen3 0.6Bqwen3✅ modelo razonador — emite <think>…</think> crudo; presupuestá SYNSEMA_LLM_MAX_TOKENS para eso
Qwen3 4B Instruct-2507qwen3✅ (GGUF de 2.5 GB, corrió en 8 GB de RAM). La 4B Thinking-2507 también corre pero piensa miles de tokens por respuesta — inviable en CPU; preferí la variante Instruct
Mistral 7B Instruct v0.3llama✅ su template [INST] se detecta solo
Llama 3.2 1B Instructllama✅ template llama3 detectado solo
SmolLM2 135M Instructllama✅ chatml
DeepSeek-R1-Distill-Qwen 1.5Bqwen2✅ corre en modo plain; filtrá los tags <think> en tu código
TinyLlama 1.1B Chatllama⚠️ carga y corre, pero su template zephyr no se reconoce → cae a plain y se comporta como modelo base

Una arch soportada carga y corre; la usabilidad como chat además necesita un template reconocido (chatml / llama3 / [INST]; si no, fallback plain). Los GGUF de gemma/phi/glm se rechazan a propósito: candle todavía no expone un reset público del KV-cache para ellos, y el aislamiento entre requests va primero.

Tip — los modelos bajados con ollama son blobs GGUF planos (y su CDN es mucho más rápido que bajar de HF con una sola conexión): ollama pull llama3.2:1b, y apuntá SYNSEMA_LLM_MODEL al blob en ~/.ollama/models/blobs/sha256-… (el digest está en el manifest bajo ~/.ollama/models/manifests/…; ollama no necesita estar corriendo).

En un binario sin la feature, SYNSEMA_LLM_PROVIDER=local avisa por stderr y queda offline — jamás degrada en silencio a otro proveedor.

Compilalo — y compilalo rápido

# build simple (funciona en cualquier CPU, pero los kernels AVX2 de candle quedan APAGADOS — prefill lento):
cargo install --path crates/synsema-cli --features llm-local --force

# prefill ~3× más rápido: candle elige sus kernels cuantizados AVX2 en tiempo de COMPILACIÓN,
# y el target x86-64 default de Rust no los habilita:
RUSTFLAGS="-C target-cpu=native" cargo install --path crates/synsema-cli --features llm-local --force

Medido (Qwen2.5 Q4_K_M): generación ~13 tok/s (0.5B) / ~5 tok/s (3B); prefill con el flag ~35 tok/s (0.5B) — pensado para prompts cortos (cientos de tokens, no miles). La carga del modelo se paga una vez por proceso (~6s / ~19s): bajo serve, la primera request carga y el resto reusa. RAM: ~1GB (0.5B) / ~2.4GB (3B). native ata el binario a la CPU de esa máquina — ideal para tu propio VPS; para un binario que distribuís, usá -C target-cpu=x86-64-v3 (AVX2+FMA, CPUs x86 desde ~2015).

---

El egress al host configurado es parte de require llmno un grant net aparte (el proveedor embebido no necesita egress en absoluto). Offline (sin clave), las ops devuelven placeholders; ramificá con llm_available(). Para saltarte las ops integradas y pegarle a la API vos mismo, mirá API del proveedor directa.