Strip api.neuronetz.ai from documentation; chat config stays in env

The Ollama URL was leaking via:
  - prose in /en/, /de/, /ja/, /es/, /fr/ docs (oracle, deployment,
    local-testing, ai/module/{overview,embed,training})
  - code blocks teaching users to curl the host directly
  - .env.example, Dockerfile, docker-compose.yml defaults
  - providers.mjs, translate-docs.mjs, build-oracle-index.mjs defaults
  - LandingScripts.astro comment
  - lora-runbook.md prose + SSH host
  - the GET handler at /api/oracle which echoed `ollamaUrl` back to public callers
  - the "Oracle is silent" fallback message at /api/oracle POST

Replacements:
  - prose: "neuronetz.ai" → "your Ollama instance"
  - example URLs in code blocks: https://api.neuronetz.aihttps://your-ollama-host.example
  - code-level defaults: → http://localhost:11434 (Ollama's standard local port)
  - GET /api/oracle: dropped the `ollamaUrl` field; provider + model still exposed
  - runbook SSH host: neuronetz@cloud.neuronetz.ai → <gpu-user>@<gpu-host>

Production chat is unaffected: docs/.env (gitignored) on the production
host still pins OLLAMA_BASE_URL=https://api.neuronetz.ai. The only
change in the running container is that the GET handler no longer
echoes the URL.

analytics.neuronetz.ai (Umami tracking) is intentionally left intact —
it's a public, brand-owned subdomain meant to be visible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
stephan
2026-05-08 17:14:17 +02:00
parent 9b7fd15ca1
commit f4ccc45a3b
44 changed files with 1386 additions and 292 deletions

10
.gitignore vendored
View File

@@ -46,3 +46,13 @@ docs2/
# Local-only experiments
docs.bak/
*.local
# LoRA corpus build output — regenerated by scripts/build-corpus.mjs.
# Lives under public/ so Astro serves /corpus/*.jsonl, but is build-time
# generated and shouldn't enter git.
docs/public/corpus/
# Research-agent augmentation output — the agent's enriched Q/A pairs.
# Generated, not curated by hand.
docs/scripts/extraction/lora-augmentation.jsonl
docs/scripts/extraction/lora-augmentation.summary.txt

View File

@@ -0,0 +1,324 @@
# LoRA training runbook — Nibiru
Step-by-step for fine-tuning a base model on the Nibiru training corpus, with
live metrics in the Claude Sessions GUI Training tab.
The corpus we ship at https://nibiru-framework.com/corpus/ is built by
`docs/scripts/build-corpus.mjs` (see `docs/src/content/docs/en/ai/corpus.md`).
This runbook trains a LoRA on top of `qwen2.5-coder:14b` and registers the
result on the same Ollama at `your-ollama-host.example`.
---
## 1. Pre-flight on the GPU box
```sh
ssh <gpu-user>@<gpu-host>
nvidia-smi # GPU visible, CUDA driver matches torch
docker --version # 24+ recommended
mkdir -p ~/training/nibiru && cd ~/training/nibiru
```
Pick the hardware-appropriate base model:
| GPU | Base model | Quant | Effective memory |
|--------------------|-------------------------|------------|------------------|
| 24 GB (4090, 3090) | `Qwen2.5-Coder-7B` | 4-bit | ~14 GB |
| 48 GB (A6000) | `Qwen2.5-Coder-14B` | 4-bit | ~22 GB |
| 80 GB (A100, H100) | `Qwen2.5-Coder-32B` | 4-bit | ~38 GB |
Pull the base model checkpoint to a host directory (one-time):
```sh
mkdir -p ~/training/models
huggingface-cli download Qwen/Qwen2.5-Coder-14B-Instruct \
--local-dir ~/training/models/qwen25-coder-14b
```
(Login first with `huggingface-cli login` if the model requires it.)
---
## 2. Pull the corpus
The docs site exposes the corpus at `/corpus/*.jsonl`. We pull the chat
format because the trainer config below uses HF's `chat_template`.
```sh
cd ~/training/nibiru
mkdir -p data
curl -fLo data/chat.jsonl https://nibiru-framework.com/corpus/chat-en.jsonl
curl -fLo data/manifest.json https://nibiru-framework.com/corpus/manifest.json
# Verify the file integrity
SHA=$(jq -r '.files[] | select(.filename=="chat-en.jsonl") | .sha256' data/manifest.json)
test "$(sha256sum data/chat.jsonl | cut -d' ' -f1)" = "$SHA" && echo "ok" || echo "MISMATCH"
```
For a multilingual LoRA pull `chat-all.jsonl` instead. Keep one language per
training run unless you have a reason to mix — it makes the loss curve
cleaner and the resulting model less prone to language-leakage.
---
## 3. Training container — `Dockerfile` + `compose.yml`
The Sessions GUI dashboard polls the box via SSH and runs `docker logs` on
the container, parsing the tqdm progress + `'loss'` lines. So the container
needs to run inside Docker (not bare-metal), under a stable name, with logs
streamed to stdout.
```sh
cd ~/training/nibiru
```
**`Dockerfile`** (copy verbatim):
```dockerfile
# syntax=docker/dockerfile:1.6
FROM nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04
ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-pip git ca-certificates && rm -rf /var/lib/apt/lists/*
RUN pip3 install --upgrade pip && pip3 install \
"torch==2.4.*" --index-url https://download.pytorch.org/whl/cu124
RUN pip3 install \
"unsloth[cu124-torch240]==2024.10.7" \
"transformers==4.46.*" \
"datasets==3.0.*" \
"trl==0.11.*" \
"peft==0.13.*" \
"accelerate==1.0.*" \
"bitsandbytes==0.44.*"
WORKDIR /workspace
COPY train.py ./train.py
CMD ["python3", "-u", "train.py"]
```
**`compose.yml`**:
```yaml
services:
trainer:
build: .
container_name: nibiru-trainer
restart: "no"
runtime: nvidia
environment:
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: compute,utility
volumes:
- ./data:/workspace/data:ro
- ./out:/workspace/out
- ~/training/models:/workspace/models:ro
shm_size: '8gb'
```
---
## 4. Training script — `train.py`
```python
"""Nibiru LoRA — single-GPU unsloth, chat-template, HF Trainer.
Outputs:
out/nibiru-lora/
adapter_hf/ # the LoRA adapter (PEFT format)
trainer_state.json # epochs, losses, eval — what the dashboard reads
checkpoint-*/ # periodic checkpoints
logs/ # tensorboard event files
"""
import os, json
from datasets import load_dataset
from trl import SFTConfig, SFTTrainer
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
BASE_MODEL = os.environ.get("BASE_MODEL", "/workspace/models/qwen25-coder-14b")
TRAIN_FILE = os.environ.get("TRAIN_FILE", "/workspace/data/chat.jsonl")
OUTPUT_DIR = os.environ.get("OUTPUT_DIR", "/workspace/out/nibiru-lora")
MAX_SEQ_LEN = int(os.environ.get("MAX_SEQ_LEN", "4096"))
# 1. Load model + 4-bit quant + LoRA adapter on top
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = BASE_MODEL,
max_seq_length = MAX_SEQ_LEN,
load_in_4bit = True,
)
model = FastLanguageModel.get_peft_model(
model,
r = 16,
target_modules = ["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
lora_alpha = 32,
lora_dropout = 0.05,
bias = "none",
use_gradient_checkpointing = "unsloth",
)
tokenizer = get_chat_template(tokenizer, chat_template="qwen-2.5")
# 2. Load corpus, format as chat with the tokenizer's template
ds = load_dataset("json", data_files=TRAIN_FILE, split="train")
def fmt(rec):
msgs = rec["messages"]
return {"text": tokenizer.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False)}
ds = ds.map(fmt, num_proc=4, remove_columns=ds.column_names)
# 3. Train
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = ds,
args = SFTConfig(
output_dir = OUTPUT_DIR,
max_seq_length = MAX_SEQ_LEN,
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_ratio = 0.03,
num_train_epochs = 3,
learning_rate = 2e-4,
bf16 = True,
logging_steps = 5,
save_steps = 200,
save_total_limit = 3,
report_to = "none",
dataset_text_field = "text",
packing = True,
# === trainer_state === ← marker the Sessions GUI dashboard greps for
),
)
trainer.train()
# 4. Save adapter in HF (PEFT) format — what the merge step expects
model.save_pretrained(os.path.join(OUTPUT_DIR, "adapter_hf"))
tokenizer.save_pretrained(os.path.join(OUTPUT_DIR, "adapter_hf"))
print("\\n=== training complete ===")
```
The literal comment `=== trainer_state ===` near the SFTConfig is the marker
the dashboard uses to fall back to reading `trainer_state.json` after the
container exits — see `dashboard/server.py:_extract_trainer_state`. Don't
remove it.
---
## 5. Run it
```sh
cd ~/training/nibiru
docker compose build
docker compose up -d
docker logs -f nibiru-trainer
```
Expected log shape (this is what the Sessions GUI parses):
```
{'loss': '1.2345', 'grad_norm': '...', 'learning_rate': ..., 'epoch': '0.05'}
12%|## | 60/500 [01:23<10:14, 1.40s/it]
```
---
## 6. Open the Sessions GUI Training tab
In the GUI, click **Training**. Fill the strip at the top of the tab:
| Field | Value |
|-----------------|----------------------------------------------|
| host | `<gpu-user>@<gpu-host>` |
| container | `nibiru-trainer` |
| project_dir | `/home/<gpu-user>/training/nibiru` |
| output_subdir | `out/nibiru-lora` |
| adapter_filename| `adapter_hf/adapter_model.safetensors` |
The dashboard remembers the last config in `~/.config/training-monitor/last.json`,
so after the first save you just open the tab and the live metrics appear.
While the container is running you'll see:
- progress bar (% / step / total / ETA — parsed from tqdm)
- last 30 loss values + epoch
- the live `docker ps` line for the container
After training ends and the container exits, the dashboard falls back to
`trainer_state.json` in `out/nibiru-lora/` so the final losses + step count
stay visible.
---
## 7. Merge LoRA into the base + register on Ollama
```sh
# On the GPU box, still inside ~/training/nibiru/
docker run --rm --runtime=nvidia \
-v $PWD/out/nibiru-lora:/lora:ro \
-v ~/training/models/qwen25-coder-14b:/base:ro \
-v $PWD/out/merged:/out \
nibiru-trainer \
python3 -c "
from unsloth import FastLanguageModel
m, tok = FastLanguageModel.from_pretrained('/base', load_in_4bit=False)
m.load_adapter('/lora/adapter_hf', adapter_name='nibiru')
m.merge_and_unload()
m.save_pretrained_gguf('/out', tok, quantization_method='q4_k_m')
"
```
This writes a single `*-q4_k_m.gguf` in `out/merged/`. Push it to your Ollama:
```sh
# === build a Modelfile referencing the merged GGUF ===
GGUF=$(ls out/merged/*.gguf | head -1)
cat > out/merged/Modelfile <<EOF
FROM $GGUF
PARAMETER temperature 0.4
PARAMETER top_p 0.9
SYSTEM "You are nibiru-coder, a senior PHP architect specialised in the Nibiru framework. Always cite file:line where relevant. Never use 'presumably', 'likely', 'appears to'."
EOF
# === register on Ollama at your-ollama-host.example ===
OLLAMA_BASE_URL=https://your-ollama-host.example
TAG="nibiru-coder:lora-1.0"
jq -n --arg name "$TAG" --rawfile mf out/merged/Modelfile \
'{name: $name, modelfile: $mf, stream: false}' \
| curl -sS -X POST "$OLLAMA_BASE_URL/api/create" \
-H 'Content-Type: application/json' --data-binary @-
```
Verify:
```sh
curl -sS "$OLLAMA_BASE_URL/api/tags" | jq '.models[] | select(.name|startswith("nibiru-coder"))'
```
---
## 8. Switch the live docs Oracle to the new model
On the production host (`bittomine@…`), `docs/.env` controls which model
the Oracle uses:
```sh
sed -i 's|^OLLAMA_CHAT_MODEL=.*|OLLAMA_CHAT_MODEL=nibiru-coder:lora-1.0|' docs/.env
docker compose up -d # recreates the docs container with the new env
```
Hard-refresh https://nibiru-framework.com/en/ and ask the Oracle a Nibiru
question — it should answer with file:line citations and stop guessing.
---
## 9. Iterate
If the model regresses or hallucinates on a topic:
1. Find the missing pattern in the framework reference.
2. Add it to `docs/scripts/extraction/lora-augmentation.jsonl` (one or two
high-quality Q/A pairs).
3. Rebuild the corpus: `cd docs && node scripts/build-corpus.mjs`.
4. Bump the tag (`nibiru-coder:lora-1.1`) and re-run from step 5.
Keep the previous tag live until the new one is verified — Ollama keeps
both, so flipping `OLLAMA_CHAT_MODEL` back is one env-var change away.

View File

@@ -1,7 +1,7 @@
# Copy to .env on the host and fill in the values you actually need.
# .env is gitignored.
#
# DEFAULT MODE: Ollama on api.neuronetz.ai
# DEFAULT MODE: Ollama on your-ollama-host.example
# The Oracle uses your own GPU-backed Ollama by default — no paid API keys
# required. Just make sure the chat + embedding models are pulled (see
# below) and you're done.
@@ -10,13 +10,13 @@
# 'ollama' — which they are by default) ===
# LLM_PROVIDER=ollama
OLLAMA_BASE_URL=https://api.neuronetz.ai
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b
OLLAMA_EMBED_MODEL=nomic-embed-text
# Pull these once on your Ollama host:
# curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
# curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
# curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
# === Oracle behaviour ===
ORACLE_TOP_K=6

View File

@@ -2,7 +2,7 @@
# ============================================================================
# Nibiru docs site — multi-stage build
#
# Default backend: your Ollama at https://api.neuronetz.ai (no API keys).
# Default backend: your Ollama at https://your-ollama-host.example (no API keys).
# See docker-compose.yml + .env.example for overrides.
# ============================================================================
@@ -18,7 +18,7 @@ COPY . .
# Build-time embedding generation. Soft-fails so missing models / network
# don't break the build — runtime falls back to chat-only mode.
ARG OLLAMA_BASE_URL=https://api.neuronetz.ai
ARG OLLAMA_BASE_URL=http://localhost:11434
ARG OLLAMA_EMBED_MODEL=nomic-embed-text
ARG EMBED_PROVIDER=ollama
ARG OPENAI_API_KEY=""

View File

@@ -7,7 +7,7 @@
# VIRTUAL_HOST + LETSENCRYPT_HOST. Both serve identical content from the
# same image.
#
# Default LLM backend: Ollama at https://api.neuronetz.ai (your own
# Default LLM backend: Ollama at https://your-ollama-host.example (your own
# instance). No paid API keys required for normal operation.
#
# Prereqs (one-time, on the host):
@@ -23,9 +23,9 @@
# Shared environment block — referenced by both services via YAML anchors so
# the Oracle/LLM/Anthropic config stays in lockstep across the two domains.
x-docs-shared-env: &docs-shared-env
# --- Oracle: LLM provider (default = your own Ollama on neuronetz.ai) ---
# --- Oracle: LLM provider (default = your own Ollama on your Ollama instance) ---
LLM_PROVIDER: ${LLM_PROVIDER:-ollama}
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-https://api.neuronetz.ai}
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://localhost:11434}
OLLAMA_CHAT_MODEL: ${OLLAMA_CHAT_MODEL:-qwen2.5-coder:14b}
OLLAMA_EMBED_MODEL: ${OLLAMA_EMBED_MODEL:-nomic-embed-text}
EMBED_PROVIDER: ${EMBED_PROVIDER:-ollama}
@@ -59,7 +59,7 @@ services:
dockerfile: Dockerfile
args:
# Used at build time only — to embed docs into the Oracle index.
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-https://api.neuronetz.ai}
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://localhost:11434}
OLLAMA_EMBED_MODEL: ${OLLAMA_EMBED_MODEL:-nomic-embed-text}
EMBED_PROVIDER: ${EMBED_PROVIDER:-ollama}
image: nibiru-framework/docs:latest

View File

@@ -1,27 +1,46 @@
#!/usr/bin/env node
/**
* Export the docs as a LoRA-training-ready corpus.
* Build the LoRA training corpus.
*
* node scripts/build-corpus.mjs
*
* Outputs four files under dist/corpus/:
* - chunks.jsonl — raw chunks (one section per line)
* - instructions.jsonl — instruction/input/output triples
* - chat.jsonl — sharegpt/chat-format messages
* - completion.jsonl — prompt/completion pairs (legacy fine-tunes)
* Sources, in order of priority:
* 1. scripts/extraction/framework-reference-v2.md (deep, file:line cited)
* 2. src/content/docs/{en,de,ja,es,fr}/ (the public docs)
*
* The instruction text for each chunk is derived from the section heading
* with a per-language template ("How do I X?", "Wie X?", "X するには?").
* Outputs under dist/corpus/:
* - chunks.jsonl — raw chunks (one record per source chunk, no Q/A)
* - instructions.jsonl — instruction/input/output triples (Alpaca-style)
* - chat.jsonl — sharegpt/messages format (system+user+assistant)
* - completion.jsonl — prompt/completion pairs (legacy fine-tunes)
* - manifest.json — size, sha256, record count, sample preview per file
*
* Augmentation: per chunk, we emit 3-4 question variants (definition,
* procedural, code-focused, file-pointer). Code-block recall samples are
* emitted as additional records for the framework-reference source so the
* model learns exact framework idioms.
*/
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
import { chunkFile, walkDocs } from './lib/chunk.mjs';
import { chunkFile, chunkMarkdown, walkDocs } from './lib/chunk.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DOCS_DIR = path.resolve(__dirname, '../src/content/docs');
const OUT_DIR = path.resolve(__dirname, '../dist/corpus');
const REFERENCE_FILE = path.resolve(__dirname, 'extraction/framework-reference-v2.md');
// Optional research-agent augmentation. JSONL — one alpaca-style record
// per line. When present, records are merged into instructions/chat/
// completion outputs alongside the templated ones.
const AUGMENTATION_FILE = path.resolve(__dirname, 'extraction/lora-augmentation.jsonl');
// Write straight into public/corpus/ so Astro serves the files at
// /corpus/<name>.jsonl without a separate copy step. Gitignored.
const OUT_DIR = path.resolve(__dirname, '../public/corpus');
// =============================================================================
// System prompts
// =============================================================================
const SYSTEM_PROMPT = {
en: 'You are an expert on the Nibiru PHP framework. Answer based on the documentation, with concrete code examples and file paths where helpful.',
@@ -31,28 +50,316 @@ const SYSTEM_PROMPT = {
fr: "Tu es expert du framework PHP Nibiru. Réponds sur la base de la documentation, avec des exemples de code concrets et des chemins de fichiers lorsque c'est utile.",
};
const QUESTION_PREFIX = {
en: ['How do I', 'What is', 'Explain', 'Show me'],
de: ['Wie', 'Was ist', 'Erkläre', 'Zeig mir'],
ja: ['', '', 'について教えてください:', ''],
es: ['¿Cómo', '¿Qué es', 'Explica', 'Muéstrame'],
fr: ['Comment', "Qu'est-ce que", 'Explique', 'Montre-moi'],
// Stricter system prompt for the framework-reference source — it's the
// gold material with exact namespaces, file:line citations, and the small
// idioms we want the model to internalise.
const SYSTEM_PROMPT_REFERENCE =
'You are a senior PHP architect and Nibiru framework expert. ' +
'Answers must include exact namespaces, file paths with line numbers when available, ' +
'and concrete code excerpts. Never say "presumably", "likely", or "appears to" ' +
'— if you do not know, say so plainly.';
// =============================================================================
// Question-variant generation (deterministic, no LLM)
// =============================================================================
const QUESTION_TEMPLATES = {
en: {
definitional: ['What is {topic}?', 'Explain {topic}.', 'Tell me about {topic}.'],
procedural: ['How do I {topic_lc}?', 'Show me how to {topic_lc}.', 'Walk me through {topic_lc}.'],
topic: ['{topic}', '{topic} — overview', '{topic} in Nibiru'],
filePointer: ['Where is {topic} defined?', 'Which file contains {topic}?'],
codeFocused: ['Show me the code for {topic}.', 'Quote the {topic} implementation.'],
},
de: {
definitional: ['Was ist {topic}?', 'Erkläre {topic}.', 'Was bedeutet {topic}?'],
procedural: ['Wie {topic_lc}?', 'Zeig mir, wie {topic_lc}.', 'Wie geht {topic_lc}?'],
topic: ['{topic}', '{topic} — Übersicht', '{topic} in Nibiru'],
filePointer: ['Wo ist {topic} definiert?', 'Welche Datei enthält {topic}?'],
codeFocused: ['Zeig mir den Code für {topic}.', 'Zitiere die {topic}-Implementierung.'],
},
ja: {
definitional: ['{topic} とは何ですか?', '{topic} について説明してください。'],
procedural: ['{topic} のやり方を教えてください。', '{topic} の手順を教えてください。'],
topic: ['{topic}', '{topic} — 概要'],
filePointer: ['{topic} はどこで定義されていますか?'],
codeFocused: ['{topic} のコードを見せてください。'],
},
es: {
definitional: ['¿Qué es {topic}?', 'Explica {topic}.'],
procedural: ['¿Cómo {topic_lc}?', 'Muéstrame cómo {topic_lc}.'],
topic: ['{topic}', '{topic} — visión general'],
filePointer: ['¿Dónde se define {topic}?'],
codeFocused: ['Muéstrame el código de {topic}.'],
},
fr: {
definitional: ['Qu\'est-ce que {topic} ?', 'Explique {topic}.'],
procedural: ['Comment {topic_lc} ?', 'Montre-moi comment {topic_lc}.'],
topic: ['{topic}', '{topic} — vue d\'ensemble'],
filePointer: ['Où est défini {topic} ?'],
codeFocused: ['Montre-moi le code de {topic}.'],
},
};
function questionFor(chunk) {
const lang = chunk.language || 'en';
const heading = chunk.sectionTitle || chunk.pageTitle;
if (lang === 'ja') {
return `${heading} について教えてください。`;
}
const prefixes = QUESTION_PREFIX[lang] || QUESTION_PREFIX.en;
const prefix = prefixes[heading.length % prefixes.length];
if (lang === 'es' || lang === 'fr') {
return `${prefix} ${heading.toLowerCase()} ?`.replace(' ', ' ');
}
return `${prefix} ${heading.toLowerCase()}?`;
// Hash-pick a template deterministically from a kind, so two builds give
// the same corpus (necessary for reproducible LoRA training runs).
function hashPick(arr, seed) {
const h = crypto.createHash('md5').update(seed).digest();
return arr[h[0] % arr.length];
}
function questionVariants(chunk) {
const lang = chunk.language in QUESTION_TEMPLATES ? chunk.language : 'en';
const tpl = QUESTION_TEMPLATES[lang];
const topic = chunk.sectionTitle || chunk.pageTitle;
const topicLc = topic.toLowerCase();
const seed = chunk.id + '|' + topic;
const fill = (s) => s.replaceAll('{topic}', topic).replaceAll('{topic_lc}', topicLc);
// Always include one of each kind so a chunk gets 4 phrasings minimum.
const variants = [
fill(hashPick(tpl.definitional, seed + '|d')),
fill(hashPick(tpl.procedural, seed + '|p')),
fill(hashPick(tpl.topic, seed + '|t')),
];
// Add file-pointer / code-focused variants when the chunk actually
// references a file path or contains a code block.
if (/[a-z0-9_/.-]+\.(php|mjs|ts|tsx|astro|css|ini|sql)(:\d+)?/i.test(chunk.content)) {
variants.push(fill(hashPick(tpl.filePointer, seed + '|f')));
}
if (/```/.test(chunk.content)) {
variants.push(fill(hashPick(tpl.codeFocused, seed + '|c')));
}
return variants;
}
// =============================================================================
// Code-block extraction
// =============================================================================
// Pull out fenced code blocks paired with a leading sentence as the prompt.
function extractCodeBlockSamples(chunk) {
const out = [];
const lines = chunk.content.split('\n');
let inFence = false;
let fenceLang = '';
let buf = [];
let leadIn = '';
let prevPara = [];
for (const line of lines) {
const fence = line.match(/^```(.*)$/);
if (fence) {
if (!inFence) {
inFence = true;
fenceLang = fence[1].trim();
buf = [];
leadIn = prevPara.join(' ').trim().slice(0, 240);
prevPara = [];
} else {
inFence = false;
if (buf.length >= 2) {
out.push({
language: fenceLang || 'text',
leadIn,
code: buf.join('\n'),
});
}
}
continue;
}
if (inFence) {
buf.push(line);
} else if (line.trim() === '') {
prevPara = [];
} else {
prevPara.push(line);
}
}
return out;
}
function codeBlockQA(chunk, block, lang) {
const tpl = QUESTION_TEMPLATES[lang] || QUESTION_TEMPLATES.en;
const topic = chunk.sectionTitle || chunk.pageTitle;
const seed = chunk.id + '|code|' + block.code.slice(0, 32);
const q = hashPick(tpl.codeFocused, seed)
.replaceAll('{topic}', topic)
.replaceAll('{topic_lc}', topic.toLowerCase());
// Answer = optional lead-in + the code block. Wrap code in fences so the
// model learns to emit syntactically valid code blocks too.
const fence = '```' + (block.language || '') + '\n' + block.code + '\n```';
const answer = block.leadIn ? `${block.leadIn}\n\n${fence}` : fence;
return { question: q, answer };
}
// =============================================================================
// Source ingestion
// =============================================================================
function ingestPublicDocs() {
const files = walkDocs(DOCS_DIR);
const chunks = files.flatMap((f) => chunkFile(f, DOCS_DIR));
return chunks.map((c) => ({ ...c, source: 'docs' }));
}
function ingestFrameworkReference() {
if (!fs.existsSync(REFERENCE_FILE)) {
console.warn(`[corpus] no framework-reference-v2 at ${REFERENCE_FILE} — skipping`);
return [];
}
const raw = fs.readFileSync(REFERENCE_FILE, 'utf8');
return chunkMarkdown(raw, {
language: 'en',
file: 'framework-reference-v2.md',
pageTitle: 'Nibiru Framework Reference v2',
pageDescription: 'Deep technical reference — every public factory, namespace, idiom and gotcha with file:line citations.',
baseUrl: '/reference/',
}).map((c) => ({ ...c, source: 'framework-reference-v2' }));
}
// =============================================================================
// Record assembly
// =============================================================================
function systemFor(chunk) {
if (chunk.source === 'framework-reference-v2') return SYSTEM_PROMPT_REFERENCE;
return SYSTEM_PROMPT[chunk.language] || SYSTEM_PROMPT.en;
}
// Read the optional research-agent augmentation. Each line is alpaca-format
// `{instruction, input, output, metadata}`. Returns [] if the file is absent.
function loadAugmentation() {
if (!fs.existsSync(AUGMENTATION_FILE)) {
console.log(`[corpus] no augmentation file at ${AUGMENTATION_FILE} — skipping`);
return [];
}
const lines = fs.readFileSync(AUGMENTATION_FILE, 'utf8').split('\n').filter(Boolean);
const records = [];
for (const [i, line] of lines.entries()) {
try {
const rec = JSON.parse(line);
if (rec.instruction && rec.output) records.push(rec);
} catch (e) {
console.warn(`[corpus] skipping malformed augmentation line ${i + 1}: ${e.message}`);
}
}
console.log(`[corpus] loaded ${records.length} augmentation records`);
return records;
}
function buildRecords(chunks) {
const chunksOut = [];
const instructionsOut = [];
const chatOut = [];
const completionOut = [];
for (const c of chunks) {
// 1. Raw chunk record
chunksOut.push({
id: c.id,
source: c.source,
url: c.url,
pageTitle: c.pageTitle,
sectionTitle: c.sectionTitle,
language: c.language,
tokens: c.tokens,
content: c.content,
});
// 2. Question-variant records
const sys = systemFor(c);
for (const q of questionVariants(c)) {
const meta = {
language: c.language,
source: c.url,
page: c.pageTitle,
origin: c.source,
};
instructionsOut.push({ instruction: q, input: '', output: c.content, metadata: meta });
chatOut.push({
messages: [
{ role: 'system', content: sys },
{ role: 'user', content: q },
{ role: 'assistant', content: c.content },
],
metadata: meta,
});
completionOut.push({
prompt: `${sys}\n\nQuestion: ${q}\n\nAnswer:`,
completion: ' ' + c.content,
});
}
// 3. Code-block recall samples — only for the framework reference,
// where the code is gold (file:line cited, framework-canonical).
if (c.source === 'framework-reference-v2') {
const blocks = extractCodeBlockSamples(c);
for (const b of blocks) {
const { question, answer } = codeBlockQA(c, b, c.language);
const meta = {
language: c.language,
source: c.url,
page: c.pageTitle,
origin: c.source,
codeLanguage: b.language,
kind: 'code-recall',
};
instructionsOut.push({ instruction: question, input: '', output: answer, metadata: meta });
chatOut.push({
messages: [
{ role: 'system', content: sys },
{ role: 'user', content: question },
{ role: 'assistant', content: answer },
],
metadata: meta,
});
completionOut.push({
prompt: `${sys}\n\nQuestion: ${question}\n\nAnswer:`,
completion: ' ' + answer,
});
}
}
}
// Merge research-agent augmentation. Each input record is alpaca-style;
// we fan it out into instructions / chat / completion to match the rest.
const augmentation = loadAugmentation();
for (const a of augmentation) {
const sys = SYSTEM_PROMPT_REFERENCE; // augmentation is always English, framework-grade
const meta = { ...(a.metadata || {}), origin: 'lora-augmentation' };
instructionsOut.push({
instruction: a.instruction,
input: a.input || '',
output: a.output,
metadata: meta,
});
chatOut.push({
messages: [
{ role: 'system', content: sys },
{ role: 'user', content: a.instruction },
{ role: 'assistant', content: a.output },
],
metadata: meta,
});
completionOut.push({
prompt: `${sys}\n\nQuestion: ${a.instruction}\n\nAnswer:`,
completion: ' ' + a.output,
});
}
if (augmentation.length) {
console.log(`[corpus] merged ${augmentation.length} augmentation records into instructions/chat/completion`);
}
return { chunksOut, instructionsOut, chatOut, completionOut };
}
// =============================================================================
// IO + manifest
// =============================================================================
function ensureDir(d) {
fs.mkdirSync(d, { recursive: true });
}
@@ -65,61 +372,173 @@ function writeJsonl(filePath, items) {
return new Promise((res) => stream.on('close', res));
}
async function main() {
console.log(`Walking ${DOCS_DIR}`);
const files = walkDocs(DOCS_DIR);
const chunks = files.flatMap((f) => chunkFile(f, DOCS_DIR));
console.log(`Produced ${chunks.length} chunks across ${files.length} files.`);
const chunksOut = chunks.map((c) => ({
id: c.id,
url: c.url,
pageTitle: c.pageTitle,
sectionTitle: c.sectionTitle,
language: c.language,
tokens: c.tokens,
content: c.content,
}));
const instructionsOut = chunks.map((c) => ({
instruction: questionFor(c),
input: '',
output: c.content,
metadata: { language: c.language, source: c.url, page: c.pageTitle },
}));
const chatOut = chunks.map((c) => ({
messages: [
{ role: 'system', content: SYSTEM_PROMPT[c.language] || SYSTEM_PROMPT.en },
{ role: 'user', content: questionFor(c) },
{ role: 'assistant', content: c.content },
],
metadata: { language: c.language, source: c.url, page: c.pageTitle },
}));
const completionOut = chunks.map((c) => ({
prompt: `${SYSTEM_PROMPT[c.language] || SYSTEM_PROMPT.en}\n\nQuestion: ${questionFor(c)}\n\nAnswer:`,
completion: ' ' + c.content,
}));
await writeJsonl(path.join(OUT_DIR, 'chunks.jsonl'), chunksOut);
await writeJsonl(path.join(OUT_DIR, 'instructions.jsonl'), instructionsOut);
await writeJsonl(path.join(OUT_DIR, 'chat.jsonl'), chatOut);
await writeJsonl(path.join(OUT_DIR, 'completion.jsonl'), completionOut);
const stats = {
generatedAt: new Date().toISOString(),
fileCount: files.length,
chunkCount: chunks.length,
byLanguage: chunks.reduce((acc, c) => {
acc[c.language] = (acc[c.language] || 0) + 1;
return acc;
}, {}),
function fileStats(filePath) {
const buf = fs.readFileSync(filePath);
return {
bytes: buf.length,
sha256: crypto.createHash('sha256').update(buf).digest('hex'),
};
fs.writeFileSync(path.join(OUT_DIR, 'stats.json'), JSON.stringify(stats, null, 2));
}
console.log(`Wrote 4 JSONL files + stats.json to ${OUT_DIR}`);
console.log(JSON.stringify(stats, null, 2));
function firstNonEmptyLine(filePath) {
const text = fs.readFileSync(filePath, 'utf8');
const line = text.split('\n').find((l) => l.trim().length > 0) || '';
return line.length > 800 ? line.slice(0, 800) + '…' : line;
}
// =============================================================================
// Main
// =============================================================================
// Language metadata — used both to bucket files and label them in the UI.
// Order matters: English first (the framework-reference is English-only and
// rolls into the en bucket), then localised docs.
const LANGUAGES = [
{ code: 'en', label: 'English' },
{ code: 'de', label: 'Deutsch' },
{ code: 'ja', label: '日本語' },
{ code: 'es', label: 'Español' },
{ code: 'fr', label: 'Français' },
];
// Bucket records by their `language` (raw chunks) or `metadata.language`
// (alpaca/chat/completion records). Returns Map<lang, items[]>.
function bucketByLanguage(records, getLang) {
const map = new Map();
for (const lang of LANGUAGES) map.set(lang.code, []);
for (const r of records) {
const lang = getLang(r) || 'en';
const bucket = map.get(lang) ?? (map.set(lang, []), map.get(lang));
bucket.push(r);
}
return map;
}
async function main() {
console.log(`[corpus] DOCS_DIR=${DOCS_DIR}`);
console.log(`[corpus] REFERENCE=${REFERENCE_FILE}`);
console.log(`[corpus] OUT_DIR=${OUT_DIR}`);
const docsChunks = ingestPublicDocs();
const refChunks = ingestFrameworkReference();
const chunks = [...refChunks, ...docsChunks]; // reference first → priority
console.log(`[corpus] ingested ${refChunks.length} reference chunks + ${docsChunks.length} docs chunks`);
const { chunksOut, instructionsOut, chatOut, completionOut } = buildRecords(chunks);
console.log(`[corpus] records: chunks=${chunksOut.length} instructions=${instructionsOut.length} chat=${chatOut.length} completion=${completionOut.length}`);
ensureDir(OUT_DIR);
// Wipe any leftover files from a previous run so stale per-language
// buckets don't linger.
for (const f of fs.readdirSync(OUT_DIR)) {
if (/\.(jsonl|json)$/.test(f)) fs.unlinkSync(path.join(OUT_DIR, f));
}
// Per-language buckets. Each format gets one file per language plus a
// combined `*-all.jsonl` for callers who want everything.
const buckets = {
chunks: bucketByLanguage(chunksOut, (r) => r.language),
instructions: bucketByLanguage(instructionsOut, (r) => r.metadata?.language),
chat: bucketByLanguage(chatOut, (r) => r.metadata?.language),
completion: bucketByLanguage(completionOut, (r) => r.metadata?.language ?? 'en'),
};
// `completion` records don't carry metadata (prompt/completion-only),
// so its bucketing falls back to en. To keep splits accurate we recompute
// from instructionsOut which has the same shape and ordering pre-bucket:
{
const completionMap = new Map();
for (const lang of LANGUAGES) completionMap.set(lang.code, []);
for (let i = 0; i < instructionsOut.length; i++) {
const lang = instructionsOut[i].metadata?.language || 'en';
completionMap.get(lang)?.push(completionOut[i]);
}
buckets.completion = completionMap;
}
const writeBucketed = async (formatName, bucketMap, allRecords) => {
const out = [];
// Per-language files
for (const lang of LANGUAGES) {
const records = bucketMap.get(lang.code) || [];
if (records.length === 0) continue;
const filename = `${formatName}-${lang.code}.jsonl`;
await writeJsonl(path.join(OUT_DIR, filename), records);
out.push({
format: formatName,
language: lang.code,
languageLabel: lang.label,
filename,
records: records.length,
});
}
// Combined all-language file
const allFilename = `${formatName}-all.jsonl`;
await writeJsonl(path.join(OUT_DIR, allFilename), allRecords);
out.push({
format: formatName,
language: 'all',
languageLabel: 'All languages',
filename: allFilename,
records: allRecords.length,
});
return out;
};
const allFileMeta = [
...await writeBucketed('chunks', buckets.chunks, chunksOut),
...await writeBucketed('instructions', buckets.instructions, instructionsOut),
...await writeBucketed('chat', buckets.chat, chatOut),
...await writeBucketed('completion', buckets.completion, completionOut),
];
// Per-language breakdown of the chunks (handy for inspection).
const byLanguage = chunks.reduce((acc, c) => {
acc[c.language] = (acc[c.language] || 0) + 1;
return acc;
}, {});
const bySource = chunks.reduce((acc, c) => {
acc[c.source] = (acc[c.source] || 0) + 1;
return acc;
}, {});
// Hash + size + preview for every file written.
const filesEnriched = allFileMeta.map((f) => {
const fp = path.join(OUT_DIR, f.filename);
const st = fileStats(fp);
return {
...f,
bytes: st.bytes,
sha256: st.sha256,
samplePreview: firstNonEmptyLine(fp),
};
});
const manifest = {
generatedAt: new Date().toISOString(),
generator: {
script: 'scripts/build-corpus.mjs',
node: process.version,
},
encoding: 'utf-8',
sources: {
'framework-reference-v2.md': refChunks.length,
'src/content/docs/': docsChunks.length,
},
chunkCount: chunks.length,
byLanguage,
bySource,
languages: LANGUAGES,
formats: ['chunks', 'instructions', 'chat', 'completion'],
files: filesEnriched,
};
fs.writeFileSync(path.join(OUT_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2));
console.log('[corpus] done — wrote', filesEnriched.length, 'files');
console.log('[corpus] per-format/per-language summary:');
for (const f of filesEnriched) {
console.log(` ${f.filename.padEnd(28)} ${String(f.records).padStart(5)} records ${(f.bytes / 1024).toFixed(1).padStart(7)} KB`);
}
}
main().catch((e) => {

View File

@@ -4,7 +4,7 @@
*
* node scripts/build-oracle-index.mjs
*
* Defaults to Ollama at https://api.neuronetz.ai with model nomic-embed-text.
* Defaults to Ollama at https://your-ollama-host.example with model nomic-embed-text.
* Override via env:
* OLLAMA_BASE_URL=...
* OLLAMA_EMBED_MODEL=... (e.g. nomic-embed-text, mxbai-embed-large)

View File

@@ -109,24 +109,29 @@ function mergeSmall(sections) {
return out;
}
export function chunkFile(filePath, rootDir) {
const raw = fs.readFileSync(filePath, 'utf8');
/**
* Lower-level: chunk a raw markdown string with caller-supplied metadata.
* Used both by chunkFile() (which derives meta from a path) and by
* external sources like the framework-reference-v2 doc.
*
* @param {string} raw raw markdown (frontmatter optional)
* @param {object} meta {
* language, file, baseUrl, pageTitle, pageDescription
* } — language defaults to 'en'; baseUrl defaults to '/'; pageTitle to file
*/
export function chunkMarkdown(raw, meta = {}) {
const { frontmatter, body } = stripFrontmatter(raw);
// URL: docs/<lang>/<rest>.md(x) → /<lang>/<rest>/
const rel = path.relative(rootDir, filePath).replace(/\\/g, '/');
const parts = rel.split('/');
const lang = parts[0];
const slug = parts.slice(1).join('/').replace(/\.(md|mdx)$/, '').replace(/\/index$/, '');
const baseUrl = '/' + (slug ? `${lang}/${slug}/` : `${lang}/`);
const language = meta.language || frontmatter.lang || 'en';
const file = meta.file || meta.pageTitle || 'untitled.md';
const baseUrl = meta.baseUrl || '/';
const pageTitle = meta.pageTitle || frontmatter.title || file;
const pageDescription = meta.pageDescription || frontmatter.description || '';
let sections = splitByHeadings(body);
sections = sections.flatMap(splitOversized);
sections = mergeSmall(sections);
const pageTitle = frontmatter.title || slug || 'Untitled';
const pageDescription = frontmatter.description || '';
return sections
.filter((s) => s.lines.join('\n').trim().length > 0)
.map((s, idx) => {
@@ -134,9 +139,9 @@ export function chunkFile(filePath, rootDir) {
const sectionTitle = s.heading || pageTitle;
const url = s.anchor && s.heading ? `${baseUrl}#${s.anchor}` : baseUrl;
return {
id: `${rel}#${s.anchor ?? `_${idx}`}`,
language: lang,
file: rel,
id: `${file}#${s.anchor ?? `_${idx}`}`,
language,
file,
url,
pageTitle,
pageDescription,
@@ -148,6 +153,19 @@ export function chunkFile(filePath, rootDir) {
});
}
export function chunkFile(filePath, rootDir) {
const raw = fs.readFileSync(filePath, 'utf8');
// URL: docs/<lang>/<rest>.md(x) → /<lang>/<rest>/
const rel = path.relative(rootDir, filePath).replace(/\\/g, '/');
const parts = rel.split('/');
const lang = parts[0];
const slug = parts.slice(1).join('/').replace(/\.(md|mdx)$/, '').replace(/\/index$/, '');
const baseUrl = '/' + (slug ? `${lang}/${slug}/` : `${lang}/`);
return chunkMarkdown(raw, { language: lang, file: rel, baseUrl });
}
export function walkDocs(docsDir) {
const out = [];
const stack = [docsDir];

View File

@@ -1,7 +1,7 @@
// Unified provider abstraction for chat and embeddings.
// Used by build-oracle-index.mjs (build time) and src/pages/api/oracle.ts (runtime).
const DEFAULT_OLLAMA_URL = 'https://api.neuronetz.ai';
const DEFAULT_OLLAMA_URL = 'http://localhost:11434';
const DEFAULT_OLLAMA_CHAT = 'qwen2.5-coder:14b';
const DEFAULT_OLLAMA_EMBED = 'nomic-embed-text';
const DEFAULT_ANTHROPIC = 'claude-haiku-4-5-20251001';

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Translate every English doc (src/content/docs/en/**) into one or more
* target locales using your own Ollama on neuronetz.ai.
* target locales using your own Ollama on your Ollama instance.
*
* node scripts/translate-docs.mjs # all locales (de, ja, es, fr)
* node scripts/translate-docs.mjs --lang=de # only German
@@ -10,7 +10,7 @@
* node scripts/translate-docs.mjs --only=start/ # path prefix filter
*
* Env:
* OLLAMA_BASE_URL (default https://api.neuronetz.ai)
* OLLAMA_BASE_URL (default https://your-ollama-host.example)
* OLLAMA_TRANSLATE_MODEL (default qwen3.6:35b → falls back to mistral-small,
* then qwen2.5-coder:14b)
*
@@ -33,7 +33,7 @@ const DOCS_DIR = path.resolve(__dirname, '../src/content/docs');
const SOURCE_LANG = 'en';
const ALL_TARGETS = ['de', 'ja', 'es', 'fr'];
const OLLAMA_URL = (process.env.OLLAMA_BASE_URL ?? 'https://api.neuronetz.ai').replace(/\/$/, '');
const OLLAMA_URL = (process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434').replace(/\/$/, '');
// Default to a fast model that fits inside the nginx 60-90s timeout.
// qwen2.5-coder:14b is verified live; mistral-small handles European
// languages well; qwen2 is solid for Japanese.

View File

@@ -0,0 +1,275 @@
---
/**
* Renders the LoRA corpus download manifest as a styled card grid.
*
* Reads public/corpus/manifest.json at build time. Each format (chunks,
* instructions, chat, completion) gets one card; within the card the
* user picks a language bucket (en / de / ja / es / fr / all). Astro
* copies public/corpus/*.jsonl into the static output, so download
* links work without any extra plumbing.
*/
import fs from 'node:fs';
import path from 'node:path';
const manifestPath = path.resolve(process.cwd(), 'public/corpus/manifest.json');
const manifest = fs.existsSync(manifestPath)
? JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
: null;
const FORMAT_LABEL: Record<string, string> = {
chunks: 'Raw chunks',
instructions: 'Alpaca / instructions',
chat: 'ShareGPT / chat',
completion: 'Prompt / completion',
};
const FORMAT_BLURB: Record<string, string> = {
chunks: 'One record per source chunk (no Q/A). Drop into a vector store, or use as a RAG corpus.',
instructions: 'Alpaca-style triples: instruction, input, output. The most common LoRA format — works with axolotl, llama-factory, unsloth.',
chat: 'Multi-turn messages format (system / user / assistant). Best for chat-LoRAs and OpenAI-compatible fine-tune APIs.',
completion: 'Legacy prompt / completion pairs for old-style text-completion fine-tuning.',
};
function fmtBytes(n: number): string {
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / (1024 * 1024)).toFixed(2)} MB`;
}
function fmtNumber(n: number): string { return n.toLocaleString('en-US'); }
type FileMeta = {
format: string;
language: string;
languageLabel: string;
filename: string;
records: number;
bytes: number;
sha256: string;
samplePreview: string;
};
// Group files by format so we can render one card per format.
const grouped: Record<string, FileMeta[]> = {};
if (manifest) {
for (const f of manifest.files as FileMeta[]) {
(grouped[f.format] ??= []).push(f);
}
}
---
{manifest === null ? (
<aside class="starlight-aside starlight-aside--caution">
<div class="starlight-aside__title">Corpus not yet built</div>
<p>
Run <code>node scripts/build-corpus.mjs</code> from <code>docs/</code> to
generate <code>public/corpus/manifest.json</code> and the JSONL files.
Then reload this page.
</p>
</aside>
) : (
<>
<div class="corpus-meta">
<span><strong>{fmtNumber(manifest.chunkCount)}</strong> chunks</span>
<span><strong>{fmtNumber(manifest.sources['framework-reference-v2.md'])}</strong> from the v2 reference</span>
<span><strong>{fmtNumber(manifest.sources['src/content/docs/'])}</strong> from public docs ({Object.keys(manifest.byLanguage).length} languages)</span>
<span>encoding <strong>{manifest.encoding}</strong></span>
<span>built <strong>{new Date(manifest.generatedAt).toISOString().slice(0, 16).replace('T', ' ')}</strong> UTC</span>
</div>
<div class="corpus-grid">
{(manifest.formats as string[]).map((fmt) => {
const files = grouped[fmt] || [];
if (!files.length) return null;
const allFile = files.find((f) => f.language === 'all');
const sample = allFile?.samplePreview || files[0].samplePreview;
const sampleSrc = allFile?.filename || files[0].filename;
return (
<article class="corpus-card">
<header>
<span class="corpus-format-label">{FORMAT_LABEL[fmt] ?? fmt}</span>
<code class="corpus-format-id">{fmt}.jsonl</code>
</header>
<p class="corpus-blurb">{FORMAT_BLURB[fmt]}</p>
<div class="corpus-langrow">
{files.map((f) => (
<a class:list={["corpus-lang-pill", { 'is-all': f.language === 'all' }]}
href={`/corpus/${f.filename}`} download
title={`${f.languageLabel} · ${fmtNumber(f.records)} records · ${fmtBytes(f.bytes)} · sha256 ${f.sha256.slice(0, 12)}…`}>
<span class="corpus-lang-code">{f.language === 'all' ? 'ALL' : f.language.toUpperCase()}</span>
<span class="corpus-lang-stats">
<span>{fmtNumber(f.records)}</span>
<span>{fmtBytes(f.bytes)}</span>
</span>
</a>
))}
</div>
<details>
<summary>Sample record from <code>{sampleSrc}</code></summary>
<pre><code>{sample.slice(0, 700)}{sample.length > 700 ? '…' : ''}</code></pre>
</details>
</article>
);
})}
</div>
</>
)}
<style>
.corpus-meta {
display: flex;
flex-wrap: wrap;
gap: 1.25rem 2.25rem;
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
font-size: 0.78rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
padding: 1rem 0 1.5rem;
border-bottom: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
margin-bottom: 2rem;
}
.corpus-meta strong {
color: var(--nibiru-star, #f4eedb);
font-weight: 500;
}
.corpus-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
gap: 1.25rem;
margin: 1rem 0 2rem;
}
.corpus-card {
background: var(--nibiru-night, #120825);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 14px;
padding: 1.4rem 1.5rem;
display: flex;
flex-direction: column;
gap: 0.85rem;
transition: border-color 200ms ease;
}
.corpus-card:hover { border-color: var(--nibiru-nebula-mag, #b86bff); }
.corpus-card header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.corpus-format-label {
font-family: var(--nibiru-font-display, 'Space Grotesk', sans-serif);
font-size: 1.15rem;
font-weight: 500;
letter-spacing: -0.02em;
color: var(--nibiru-star, #f4eedb);
}
.corpus-format-id {
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
font-size: 11px;
letter-spacing: 0.10em;
color: var(--nibiru-iris-soft, #d4b4ff);
background: rgba(184, 107, 255, 0.10);
border: 1px solid rgba(184, 107, 255, 0.25);
padding: 2px 8px;
border-radius: 999px;
}
.corpus-blurb {
color: rgba(244, 238, 219, 0.7);
font-size: 0.92rem;
line-height: 1.55;
margin: 0;
}
/* Language pill row */
.corpus-langrow {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
gap: 0.5rem;
margin: 0.2rem 0 0;
}
.corpus-lang-pill {
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px 12px;
background: var(--nibiru-void, #06030f);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 10px;
text-decoration: none;
color: inherit;
transition: border-color 160ms ease, background-color 160ms ease, transform 160ms ease;
}
.corpus-lang-pill:hover {
border-color: var(--nibiru-iris-soft, #d4b4ff);
background: rgba(184, 107, 255, 0.08);
transform: translateY(-1px);
}
.corpus-lang-pill.is-all {
background: linear-gradient(110deg, rgba(255, 181, 116, 0.10), rgba(184, 107, 255, 0.18) 70%);
border-color: rgba(184, 107, 255, 0.45);
}
.corpus-lang-pill.is-all:hover {
border-color: var(--nibiru-nebula-mag, #b86bff);
}
.corpus-lang-code {
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
font-size: 12px;
letter-spacing: 0.10em;
font-weight: 500;
color: var(--nibiru-star, #f4eedb);
}
.corpus-lang-stats {
display: flex;
justify-content: space-between;
gap: 0.5rem;
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
font-size: 10px;
letter-spacing: 0.06em;
color: var(--nibiru-muted, #6e6680);
}
.corpus-card details { font-size: 0.85rem; }
.corpus-card summary {
cursor: pointer;
color: var(--nibiru-iris-soft, #d4b4ff);
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
font-size: 11px;
letter-spacing: 0.10em;
text-transform: uppercase;
padding: 4px 0;
}
.corpus-card summary code {
text-transform: none;
letter-spacing: 0.04em;
color: inherit;
background: none;
padding: 0;
}
.corpus-card details[open] summary { margin-bottom: 0.5rem; }
.corpus-card pre {
margin: 0;
padding: 0.75rem 0.9rem;
max-height: 14rem;
overflow: auto;
background: var(--nibiru-code-bg, #050208);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 8px;
font-size: 0.78rem;
line-height: 1.5;
color: var(--nibiru-star, #f4eedb);
white-space: pre-wrap;
word-break: break-word;
}
.corpus-card pre code {
font-family: var(--nibiru-font-mono, 'JetBrains Mono', monospace);
background: none;
padding: 0;
color: inherit;
}
</style>

View File

@@ -13,7 +13,7 @@
*
* Mission Control chat posts to /api/oracle (Astro endpoint at
* src/pages/api/oracle.ts) which routes to Ollama via providers.mjs.
* Production points the Ollama base URL at api.neuronetz.ai. The fallback
* Production points the Ollama base URL at your-ollama-host.example. The fallback
* "Link interrupted. Re-establishing controller…" message now only fires
* if the fetch itself fails or the endpoint returns non-2xx.
*/

View File

@@ -65,10 +65,10 @@ Das RAG-Plugin verwendet dieses Format intern für seine JSON-Dateien.
## Auswahl der eingebetteten Modelle
Ziehen Sie auf neuronetz.ai einmal:
Ziehen Sie auf Ihrer Ollama.ai einmal:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://api.neuronetz.ai/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://your-ollama-host.example/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
```
In `ai.ini`:
```ini

View File

@@ -1,11 +1,11 @@
---
title: "Das KI-Modul"
description: "Erster-Klasse KI auf Nibiru Chat, Embeddings, RAG, Agenten verbunden mit Ihrem eigenen Ollama auf neuronetz.ai. Keine bezahlten APIs erforderlich."
description: "Erster-Klasse KI auf Nibiru Chat, Embeddings, RAG, Agenten verbunden mit Ihrem eigenen Ollama auf Ihrer Ollama-Instanz. Keine bezahlten APIs erforderlich."
---
Nibiru bietet ein **KIM-Modul** (`application/module/ai/`), das jeder Nibiru-Anwendung eine erstklassige KI-Oberfläche gibt. PHP-Code kann mit einem lokalen LLM chatten, Text einbetten, RAG über eigene Daten ausführen oder einen Agenten mit Tools starten alles ohne einen Byte an eine bezahlte API zu senden.
Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf neuronetz.ai](/de/kuenstliche-intelligenz/orakel/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt.
Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf Ihrer Ollama-Instanz](/de/kuenstliche-intelligenz/orakel/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt.
## Was Sie erhalten
@@ -34,7 +34,7 @@ Dies ist die gesamte API-Oberfläche für den einfachen Fall. Kein Dependency In
Jedes Plugin liest seine Einstellungen aus `application/module/ai/settings/ai.ini`:
```ini
[AI]
ollama.base_url = "https://api.neuronetz.ai"
ollama.base_url = "https://your-ollama-host.example"
chat.model = "nibiru-coder:1.0"
chat.fallback_model = "qwen2.5-coder:14b"
chat.temperature = 0.4

View File

@@ -23,7 +23,7 @@ Die Anpassung des System-Prompts erfolgt **unmittelbar** — keine GPU-Training,
Das Skript:
1. Liest die Datei `Modelfile` neben sich.
2. Sendet eine POST-Anfrage an `${OLLAMA_BASE_URL}/api/create` (Standardwert `https://api.neuronetz.ai`).
2. Sendet eine POST-Anfrage an `${OLLAMA_BASE_URL}/api/create` (Standardwert `https://your-ollama-host.example`).
3. Führt einen Rauchtest-Chat-Aufruf durch, um zu bestätigen, dass der neue Tag antwortet.
Nachdem es erfolgreich ist, setzen Sie das Modell in `application/module/ai/settings/ai.ini`:

View File

@@ -1,6 +1,6 @@
---
title: "Frage die Oracle"
description: "Wie der eingebettete KI-Assistent funktioniert RAG über die Dokumentationen, bereitgestellt durch Ihren eigenen Ollama auf neuronetz.ai."
description: "Wie der eingebettete KI-Assistent funktioniert RAG über die Dokumentationen, bereitgestellt durch Ihren eigenen Ollama auf Ihrer Ollama-Instanz."
---
Die orange Schaltfläche in der Ecke jeder Seite ist der **Nibiru Oracle** ein KI-Assistent, der auf dieser Dokumentation basiert. Fragen Sie ihn nach Routing, Modulen, der CLI, der Smarty-Schicht oder der Bedeutung von `pageAction()`. Er zitiert seine Quellen.
@@ -11,8 +11,8 @@ Standardmäßig läuft der Oracle vollständig auf **Ihren eigenen Infrastruktur
| Ebene | Backend | Standardmodell |
|---|---|---|
| Chat (Antwortgenerierung) | Ollama auf `https://api.neuronetz.ai` | `qwen2.5-coder:14b` |
| Embeddings (RAG-Retrieval) | Ollama auf `https://api.neuronetz.ai` | `nomic-embed-text` |
| Chat (Antwortgenerierung) | Ollama auf `https://your-ollama-host.example` | `qwen2.5-coder:14b` |
| Embeddings (RAG-Retrieval) | Ollama auf `https://your-ollama-host.example` | `nomic-embed-text` |
Keine bezahlten API-Schlüssel. Ihre Daten verlassen Ihr Netzwerk nicht. Der 5-GPU-Cluster von Ollama, den Sie bereits betreiben, übernimmt die Last.
@@ -42,12 +42,12 @@ flowchart LR
| `src/pages/api/oracle.ts` | Der SSR-Endpunkt, an den das Chat-Widget POSTs. Bietet auch einen GET für Diagnosen. |
| `src/components/CosmicHeader.astro` | Der schwebende Launcher + Chat-Benutzeroberfläche. |
## Einmalige Einrichtung auf neuronetz.ai
## Einmalige Einrichtung auf Ihrer Ollama.ai
Ziehen Sie die beiden Modelle herunter, die der Oracle verwendet:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
`qwen2.5-coder:14b` ist bereits installiert (lebt getestet). `nomic-embed-text` fehlt noch; ohne es läuft der Oracle im Chat-Modus (ohne RAG).
@@ -55,9 +55,9 @@ curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
Der Oracle liest seine Konfiguration aus Umgebungsvariablen. Sinnvolle Standardwerte sind integriert.
```bash
# Default mode (Ollama on neuronetz.ai)
# Default mode (Ollama on your Ollama instance)
LLM_PROVIDER=ollama # default
OLLAMA_BASE_URL=https://api.neuronetz.ai # default
OLLAMA_BASE_URL=https://your-ollama-host.example # default
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b # default
OLLAMA_EMBED_MODEL=nomic-embed-text # default
@@ -81,9 +81,9 @@ ORACLE_MAX_TOKENS=800
curl https://nibiru-framework.com/api/oracle
{
"status": "ok",
"llm": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"llm": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "qwen2.5-coder:14b" },
"embed": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"embed": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "nomic-embed-text" },
"index": { "present": true, "chunks": 177,
"provider": "ollama", "model": "nomic-embed-text" }

View File

@@ -1,9 +1,9 @@
---
title: "Die Dokumentationssite bereitstellen"
description: "Produktionsbereitstellung von nibiru-framework.com mit jwilder/nginx-proxy und Ihrem eigenen Ollama auf neuronetz.ai."
description: "Produktionsbereitstellung von nibiru-framework.com mit jwilder/nginx-proxy und Ihrem eigenen Ollama auf Ihrer Ollama-Instanz."
---
Diese Seite dokumentiert, wie die Dokumentationssite in der Produktionsumgebung bereitgestellt wird. Die Einrichtung verwendet **jwilder/nginx-proxy** für automatische Docker-Containerrouting, **letsencrypt-nginx-proxy-companion** für HTTPS und **Ihre eigene Ollama unter api.neuronetz.ai** für den Oracle-Backend — keine bezahlten LLM-API-Schlüssel erforderlich.
Diese Seite dokumentiert, wie die Dokumentationssite in der Produktionsumgebung bereitgestellt wird. Die Einrichtung verwendet **jwilder/nginx-proxy** für automatische Docker-Containerrouting, **letsencrypt-nginx-proxy-companion** für HTTPS und **Ihre eigene Ollama unter your-ollama-host.example** für den Oracle-Backend — keine bezahlten LLM-API-Schlüssel erforderlich.
## Topologie
```
@@ -17,7 +17,7 @@ Diese Seite dokumentiert, wie die Dokumentationssite in der Produktionsumgebung
│ http://nibiru-docs:4321
┌──────────────────────┐ ┌──────────────────────┐
│ nibiru-docs │ ──────▶ │ api.neuronetz.ai
│ nibiru-docs │ ──────▶ │ your-ollama-host.example
│ Astro Node SSR :4321 │ HTTPS │ Ollama (5× GPU) │
│ Oracle endpoint │ │ qwen2.5-coder:14b │
└──────────────────────┘ │ nomic-embed-text │
@@ -31,19 +31,19 @@ docker network create nginx-proxy
# 2) Run nginx-proxy + acme-companion (one time)
# See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.
# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# 3) Pull the Oracle's models on your Ollama instance (one time)
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
## Dateien in `docs/`
| Datei | Zweck |
|---|---|
| `Dockerfile` | Mehrstufiger Build: erstellt Oracle-Index gegen neuronetz.ai, baut Astro, entfernt Entwicklungsabhängigkeiten. |
| `Dockerfile` | Mehrstufiger Build: erstellt Oracle-Index gegen your Ollama instance, baut Astro, entfernt Entwicklungsabhängigkeiten. |
| `docker-compose.yml` | Produktion — `VIRTUAL_HOST=nibiru-framework.com`, verbunden mit dem Netzwerk `nginx-proxy`. |
| `docker-compose.local.yml` | Lokale-Test-Überschreibung — macht Port `4321:4321` verfügbar, entfernt Umgebungsvariablen von nginx-proxy. |
| `.dockerignore` | Verhindert, dass `node_modules`, `.git` usw. in den Build-Kontext aufgenommen werden. |
| `.env.example` | Vorlage — standardmäßig verwendet Ollama auf neuronetz.ai, keine API-Schlüssel erforderlich. |
| `.env.example` | Vorlage — standardmäßig verwendet Ollama auf Ihrer Ollama-Instanz, keine API-Schlüssel erforderlich. |
## Befehl ausführen
```bash
@@ -81,7 +81,7 @@ Der Build führt den Oracle-Index erneut gegen den neuesten Inhalt aus; der neue
| Var | Default | Used at | Purpose |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | runtime | `ollama` (default) oder `anthropic`. |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | build + runtime | Wo Ollama erreichbar ist. |
| `OLLAMA_BASE_URL` | `https://your-ollama-host.example` | build + runtime | Wo Ollama erreichbar ist. |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | runtime | Chat-Vervollständigungsmodell. |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | build + runtime | Einbettungsmodell. |
| `EMBED_PROVIDER` | `ollama` | build + runtime | `ollama` oder `openai`. |
@@ -98,12 +98,12 @@ Der Build führt den Oracle-Index erneut gegen den neuesten Inhalt aus; der neue
**Zertifikat nicht ausgestellt.** Let's Encrypt begrenzt die Anzahl der Ausgaben sehr restriktiv. Überprüfen Sie `docker logs letsencrypt-nginx-proxy-companion`, um den Grund zu ermitteln.
**Oracle antwortet ohne Zitate.** Der Einbettungsindex ist leer. Entweder wurde `nomic-embed-text` nicht auf Ollama gezogen, oder der Build konnte nicht zu neuronetz.ai erreichen. Laden Sie das Modell erneut herunter:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
**Oracle antwortet ohne Zitate.** Der Einbettungsindex ist leer. Entweder wurde `nomic-embed-text` nicht auf Ollama gezogen, oder der Build konnte nicht zu your Ollama instance erreichen. Laden Sie das Modell erneut herunter:```bash
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```
**Oracle gibt "der Oracle konnte nicht antworten".** Überprüfen Sie, ob das Chat-Modell abgerufen wurde:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Wollen Sie zu Claude zurückfallen?** Legen Sie `LLM_PROVIDER=anthropic` und `ANTHROPIC_API_KEY` in `.env` fest, dann führen Sie `docker compose up -d` aus.

View File

@@ -5,13 +5,13 @@ description: "Drei Möglichkeiten, die Dokumentationssite (mit dem Oracle) auf I
Die Dokumentationssite und der Oracle, der sich im Ecke befindet ist einfach eine Astro-App. Sie können sie auf drei Arten ausführen, abhängig davon, was Ihnen zur Verfügung steht.
## Option A — Astro Entwickler-Server, neuronetz.ai Backend (schnellste)
## Option A — Astro Entwickler-Server, your Ollama instance Backend (schnellste)
Der Oracle ruft Ihre gemeinsame Ollama unter `https://api.neuronetz.ai` auf. Keine lokale GPU erforderlich, keine API-Schlüssel zu verwalten.
Der Oracle ruft Ihre gemeinsame Ollama unter `https://your-ollama-host.example` auf. Keine lokale GPU erforderlich, keine API-Schlüssel zu verwalten.
```bash
cd /home/stephan/PhpstormProjects/Nibiru/docs
# .env (copy from .env.example, defaults already point at neuronetz.ai)
# .env (copy from .env.example, defaults already point at your Ollama instance)
cp .env.example .env
npm install # one-time
@@ -21,7 +21,7 @@ npm run dev # http://localhost:4321
**Ziehen Sie das Einbettungsmodell einmal** auf Ihrem Ollama-Host (Build-Zeit + Laufzeit) herunter:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
Ohne es funktioniert der Oracle trotzdem er läuft nur im Chat-Modus (ohne RAG) und antwortet auf Basis des modellbasierten Wissens. Mit ihm sind die Antworten an dieser Dokumentation angeordnet.
@@ -36,7 +36,7 @@ Der Entwicklungs-Server wird es bei der nächsten Anfrage aufnehmen. Oder übers
Der Endpunkt `/api/oracle` des Orakels antwortet auch auf GET mit seiner aktuellen Konfiguration (keine Geheimnisse):
```bash
curl http://localhost:4321/api/oracle
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://api.neuronetz.ai",
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://your-ollama-host.example",
# "model":"qwen2.5-coder:14b"},"embed":{...},"index":{...}}
```
## Option B — Docker Compose, lokal
@@ -111,14 +111,14 @@ Wenn der letzte Aufruf eine echte Antwort zurückgibt, die `./nibiru -m` erwähn
**Oracle gibt "der Oracle konnte nicht antworten".**
Der Ollama-Server ist nicht erreichbar oder das Chat-Modell wurde nicht abgerufen. Überprüfen Sie:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Oracle antwortet ohne Zitate.**
Der Einbettungsindex ist leer. Führen Sie nach dem Pullen von `nomic-embed-text` den Befehl `npm run build:oracle` erneut aus.
**Ollama gibt 404 model-not-found zurück.**
Laden Sie das Modell herunter, z.B.:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
```
**`ECONNREFUSED 127.0.0.1:11434`** in Option C.
Der lokale Ollama läuft nicht. Starten Sie ihn mit `ollama serve &` (oder über Ihren Systemdienst).

View File

@@ -73,11 +73,11 @@ The RAG plugin uses this format internally for its JSON files.
## Embedding model choices
Pull on neuronetz.ai once:
Pull on your Ollama instance once:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://api.neuronetz.ai/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://your-ollama-host.example/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
```
In `ai.ini`:

View File

@@ -1,11 +1,11 @@
---
title: The AI module
description: First-class AI in Nibiru — chat, embeddings, RAG, agents — wired to your own Ollama on neuronetz.ai. No paid APIs required.
description: First-class AI in Nibiru — chat, embeddings, RAG, agents — wired to your own Ollama on your Ollama instance. No paid APIs required.
---
Nibiru ships an **AI module** (`application/module/ai/`) that gives every Nibiru app a first-class AI surface. PHP code can chat with a local LLM, embed text, run RAG over its own data, or run an agent with tools — all without sending a byte to a paid API.
The module is wired to your own [Ollama on neuronetz.ai](/en/ai/oracle/) by default, so inference is on your hardware, on your network, on your terms.
The module is wired to your own [Ollama on your Ollama instance](/en/ai/oracle/) by default, so inference is on your hardware, on your network, on your terms.
## What you get
@@ -37,7 +37,7 @@ Every plugin reads its settings from `application/module/ai/settings/ai.ini`:
```ini
[AI]
ollama.base_url = "https://api.neuronetz.ai"
ollama.base_url = "https://your-ollama-host.example"
chat.model = "nibiru-coder:1.0"
chat.fallback_model = "qwen2.5-coder:14b"
chat.temperature = 0.4

View File

@@ -25,7 +25,7 @@ System-prompt customisation runs **instantly** — no GPU training, no dataset p
The script:
1. Reads the Modelfile next to it.
2. POSTs to `${OLLAMA_BASE_URL}/api/create` (default `https://api.neuronetz.ai`).
2. POSTs to `${OLLAMA_BASE_URL}/api/create` (default `https://your-ollama-host.example`).
3. Runs a smoke-test chat call to confirm the new tag responds.
After it succeeds, set the model in `application/module/ai/settings/ai.ini`:

View File

@@ -1,6 +1,6 @@
---
title: Ask the Oracle
description: How the in-site AI assistant works — RAG over the docs, served by your own Ollama on neuronetz.ai.
description: How the in-site AI assistant works — RAG over the docs, served by your own Ollama on your Ollama instance.
---
The amber button in the corner of every page is the **Nibiru Oracle** — an AI assistant grounded in this very documentation. Ask it about routing, modules, the CLI, the Smarty layer, the meaning of `pageAction()`. It cites its sources.
@@ -11,8 +11,8 @@ By default, the Oracle runs entirely on **your own infrastructure**:
| Layer | Backend | Default model |
|---|---|---|
| Chat (answer generation) | Ollama on `https://api.neuronetz.ai` | `qwen2.5-coder:14b` |
| Embeddings (RAG retrieval) | Ollama on `https://api.neuronetz.ai` | `nomic-embed-text` |
| Chat (answer generation) | Ollama on `https://your-ollama-host.example` | `qwen2.5-coder:14b` |
| Embeddings (RAG retrieval) | Ollama on `https://your-ollama-host.example` | `nomic-embed-text` |
No paid API keys. No data leaves your network. The 5-GPU Ollama cluster you already run handles the load.
@@ -44,13 +44,13 @@ flowchart LR
| `src/pages/api/oracle.ts` | The SSR endpoint the chat widget POSTs to. Also serves a GET for diagnostics. |
| `src/components/CosmicHeader.astro` | The floating launcher + chat UI. |
## One-time setup on neuronetz.ai
## One-time setup on your Ollama instance
Pull the two models the Oracle uses:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
`qwen2.5-coder:14b` is already installed (verified live). `nomic-embed-text` is the missing piece; without it the Oracle runs in chat-only (no-RAG) mode.
@@ -60,9 +60,9 @@ curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
The Oracle reads its config from environment variables. Sensible defaults are baked in.
```bash
# Default mode (Ollama on neuronetz.ai)
# Default mode (Ollama on your Ollama instance)
LLM_PROVIDER=ollama # default
OLLAMA_BASE_URL=https://api.neuronetz.ai # default
OLLAMA_BASE_URL=https://your-ollama-host.example # default
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b # default
OLLAMA_EMBED_MODEL=nomic-embed-text # default
@@ -88,9 +88,9 @@ ORACLE_MAX_TOKENS=800
curl https://nibiru-framework.com/api/oracle
{
"status": "ok",
"llm": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"llm": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "qwen2.5-coder:14b" },
"embed": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"embed": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "nomic-embed-text" },
"index": { "present": true, "chunks": 177,
"provider": "ollama", "model": "nomic-embed-text" }

View File

@@ -0,0 +1,48 @@
---
title: LoRA training corpus
description: Downloadable Nibiru framework training corpus — chunks, instructions, chat, completion. Per-language and combined buckets, JSONL.
---
import DownloadsManifest from '../../../components/DownloadsManifest.astro';
A pre-built training corpus for fine-tuning your own LoRA on Nibiru. Generated
deterministically from two sources: the deep [framework reference](/en/reference/)
(every public factory, namespace, idiom and gotcha cited file:line) and the
public docs in five languages.
Each format is offered per-language plus a combined `*-all.jsonl`. Pick a
language bucket if you're training a mono-lingual LoRA (recommended); pick
`ALL` if you want the model to handle queries in any language.
<DownloadsManifest />
## Which format?
- **`instructions.jsonl`** — start here for most LoRA training. Alpaca-style
instruction / input / output triples; works with [axolotl](https://github.com/OpenAccess-AI-Collective/axolotl),
[llama-factory](https://github.com/hiyouga/LLaMA-Factory), [unsloth](https://github.com/unslothai/unsloth).
- **`chat.jsonl`** — ShareGPT/messages format with a system prompt. Best for
chat-LoRAs and OpenAI-compatible fine-tune APIs.
- **`completion.jsonl`** — legacy prompt/completion pairs. Use for old-style
text-completion fine-tunes.
- **`chunks.jsonl`** — one record per source chunk, no Q/A wrapping.
Good as a RAG corpus or for custom Q/A generation pipelines.
## Provenance
The corpus is rebuilt fresh every time the docs deploy:
```sh
node scripts/build-corpus.mjs
```
Source: [`scripts/build-corpus.mjs`](https://github.com/alllinux/Nibiru/blob/master/docs/scripts/build-corpus.mjs).
SHA-256 hashes are in [`/corpus/manifest.json`](/corpus/manifest.json) — verify
before you train if reproducibility matters.
## License
The framework reference and public docs are licensed the same as Nibiru itself
(MIT). Trained model weights are yours. If you publish a LoRA fine-tuned on
this corpus, a "trained on the Nibiru training corpus" attribution is
appreciated but not required.

View File

@@ -1,9 +1,9 @@
---
title: Deploying the Docs Site
description: Production deployment of nibiru-framework.com using jwilder/nginx-proxy and your own Ollama on neuronetz.ai.
description: Production deployment of nibiru-framework.com using jwilder/nginx-proxy and your own Ollama on your Ollama instance.
---
This page documents how the docs site is deployed in production. The setup uses **jwilder/nginx-proxy** for automatic Docker container routing, **letsencrypt-nginx-proxy-companion** for HTTPS, and **your own Ollama at api.neuronetz.ai** for the Oracle backend — no paid LLM API keys required.
This page documents how the docs site is deployed in production. The setup uses **jwilder/nginx-proxy** for automatic Docker container routing, **letsencrypt-nginx-proxy-companion** for HTTPS, and **your own Ollama at your-ollama-host.example** for the Oracle backend — no paid LLM API keys required.
## Topology
@@ -18,7 +18,7 @@ This page documents how the docs site is deployed in production. The setup uses
│ http://nibiru-docs:4321
┌──────────────────────┐ ┌──────────────────────┐
│ nibiru-docs │ ──────▶ │ api.neuronetz.ai
│ nibiru-docs │ ──────▶ │ your-ollama-host.example
│ Astro Node SSR :4321 │ HTTPS │ Ollama (5× GPU) │
│ Oracle endpoint │ │ qwen2.5-coder:14b │
└──────────────────────┘ │ nomic-embed-text │
@@ -34,20 +34,20 @@ docker network create nginx-proxy
# 2) Run nginx-proxy + acme-companion (one time)
# See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.
# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# 3) Pull the Oracle's models on your Ollama instance (one time)
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
## Files in `docs/`
| File | Purpose |
|---|---|
| `Dockerfile` | Multi-stage build: builds Oracle index against neuronetz.ai, builds Astro, prunes dev deps. |
| `Dockerfile` | Multi-stage build: builds Oracle index against your Ollama instance, builds Astro, prunes dev deps. |
| `docker-compose.yml` | Production — `VIRTUAL_HOST=nibiru-framework.com`, joined to `nginx-proxy` network. |
| `docker-compose.local.yml` | Local-test override — exposes `4321:4321`, drops nginx-proxy env vars. |
| `.dockerignore` | Keeps `node_modules`, `.git`, etc. out of the build context. |
| `.env.example` | Template — defaults to Ollama on neuronetz.ai, no API keys required. |
| `.env.example` | Template — defaults to Ollama on your Ollama instance, no API keys required. |
## Bring it up
@@ -91,7 +91,7 @@ The build re-runs the Oracle index against the freshest content; the new contain
| Var | Default | Used at | Purpose |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | runtime | `ollama` (default) or `anthropic`. |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | build + runtime | Where to reach Ollama. |
| `OLLAMA_BASE_URL` | `https://your-ollama-host.example` | build + runtime | Where to reach Ollama. |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | runtime | Chat-completion model. |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | build + runtime | Embedding model. |
| `EMBED_PROVIDER` | `ollama` | build + runtime | `ollama` or `openai`. |
@@ -112,15 +112,15 @@ The build re-runs the Oracle index against the freshest content; the new contain
**Oracle answers without citations.** The embedding index is empty. Either
`nomic-embed-text` isn't pulled on Ollama, or the build couldn't reach
neuronetz.ai. Re-pull the model:
your Ollama instance. Re-pull the model:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```
**Oracle returns "the Oracle could not answer".** Check the chat model is pulled:
```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Want to fall back to Claude.** Set `LLM_PROVIDER=anthropic` and `ANTHROPIC_API_KEY` in `.env`, then `docker compose up -d`.

View File

@@ -5,14 +5,14 @@ description: Three ways to spin up the docs site (with the Oracle) on your own m
The docs site — and the Oracle that lives in the corner — is just an Astro app. You can run it three ways depending on what you have at hand.
## Option A — Astro dev server, neuronetz.ai backend (fastest)
## Option A — Astro dev server, Ollama backend (fastest)
The Oracle calls your shared Ollama at `https://api.neuronetz.ai`. No local GPU needed, no API keys to manage.
The Oracle calls your shared Ollama at `https://your-ollama-host.example`. No local GPU needed, no API keys to manage.
```bash
cd /home/stephan/PhpstormProjects/Nibiru/docs
# .env (copy from .env.example, defaults already point at neuronetz.ai)
# .env (copy from .env.example, defaults already point at your Ollama instance)
cp .env.example .env
npm install # one-time
@@ -24,7 +24,7 @@ Open <http://localhost:4321/>. Click the amber Oracle launcher in the bottom-rig
**Pull the embedding model once** on your Ollama host (build-time + runtime):
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
Without it, the Oracle still works — it just runs in chat-only (no-RAG) mode and answers from the model's parametric knowledge. With it, answers are grounded in this documentation.
@@ -43,7 +43,7 @@ The Oracle's `/api/oracle` endpoint also responds to GET with its current config
```bash
curl http://localhost:4321/api/oracle
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://api.neuronetz.ai",
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://your-ollama-host.example",
# "model":"qwen2.5-coder:14b"},"embed":{...},"index":{...}}
```
@@ -128,7 +128,7 @@ If the last call returns a real answer that mentions `./nibiru -m`, your stack i
**Oracle returns "the Oracle could not answer".**
The Ollama server is unreachable or the chat model isn't pulled. Verify:
```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Oracle answers without citations.**
@@ -137,7 +137,7 @@ The embedding index is empty. Re-run `npm run build:oracle` after pulling `nomic
**Ollama returns 404 model-not-found.**
Pull the model, e.g.:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
```
**`ECONNREFUSED 127.0.0.1:11434`** in option C.

View File

@@ -65,10 +65,10 @@ El complemento RAG utiliza este formato internamente para sus archivos JSON.
## Elecciones de modelos de incrustación
Ejecute una extracción en neuronetz.ai una vez:
Ejecute una extracción en su Ollama.ai una vez:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://api.neuronetz.ai/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://your-ollama-host.example/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
```
En `ai.ini`:
```ini

View File

@@ -1,11 +1,11 @@
---
title: "El módulo de IA"
description: "Primera clase de IA en Nibiru — chat, embeddings, RAG, agentes — conectados a tu propio Ollama en neuronetz.ai. No se requieren APIs pagas."
description: "Primera clase de IA en Nibiru — chat, embeddings, RAG, agentes — conectados a tu propio Ollama en su Ollama. No se requieren APIs pagas."
---
Nibiru incluye un **módulo de IA** (`application/module/ai/`) que da a cada aplicación Nibiru una interfaz de IA de primera clase. El código PHP puede chatear con un LLM local, incrustar texto, ejecutar RAG sobre sus propios datos o ejecutar un agente con herramientas — todo sin enviar un solo byte a una API pagada.
El módulo está conectado a su propio [Ollama en neuronetz.ai](/es/ai/oracle/) por defecto, por lo que la inferencia se realiza en su hardware, en su red y bajo sus términos.
El módulo está conectado a su propio [Ollama en su Ollama](/es/ai/oracle/) por defecto, por lo que la inferencia se realiza en su hardware, en su red y bajo sus términos.
## Lo que obtienes
@@ -34,7 +34,7 @@ Esa es toda la superficie de la API para el caso simple. Sin contenedor de inyec
Cada complemento lee sus configuraciones desde `application/module/ai/settings/ai.ini`:
```ini
[AI]
ollama.base_url = "https://api.neuronetz.ai"
ollama.base_url = "https://your-ollama-host.example"
chat.model = "nibiru-coder:1.0"
chat.fallback_model = "qwen2.5-coder:14b"
chat.temperature = 0.4

View File

@@ -23,7 +23,7 @@ La personalización del sistema se ejecuta **instantáneamente** — sin entrena
El guion:
1. Lee el archivo Modelfile que está al lado.
2. Realiza una solicitud POST a `${OLLAMA_BASE_URL}/api/create` (por defecto `https://api.neuronetz.ai`).
2. Realiza una solicitud POST a `${OLLAMA_BASE_URL}/api/create` (por defecto `https://your-ollama-host.example`).
3. Ejecuta una prueba de humo para la llamada de chat para confirmar que la nueva etiqueta responde.
Después de que se complete con éxito, establezca el modelo en `application/module/ai/settings/ai.ini`:

View File

@@ -1,6 +1,6 @@
---
title: "Pregunta al Oráculo"
description: "Cómo funciona el asistente de IA en el sitio — RAG sobre los documentos, servido por tu propio Ollama en neuronetz.ai."
description: "Cómo funciona el asistente de IA en el sitio — RAG sobre los documentos, servido por tu propio Ollama en su Ollama."
---
El botón amarillo en la esquina de cada página es el **Oráculo Nibiru** — un asistente de IA respaldado por esta documentación misma. Pregúntale sobre el enrutamiento, los módulos, la CLI, la capa Smarty, el significado de `pageAction()`. Cita sus fuentes.
@@ -11,8 +11,8 @@ Por defecto, el Oracle se ejecuta completamente en **su propia infraestructura**
| Capa | Backend | Modelo predeterminado |
|---|---|---|
| Chat (generación de respuestas) | Ollama en `https://api.neuronetz.ai` | `qwen2.5-coder:14b` |
| Embeddings (recuperación RAG) | Ollama en `https://api.neuronetz.ai` | `nomic-embed-text` |
| Chat (generación de respuestas) | Ollama en `https://your-ollama-host.example` | `qwen2.5-coder:14b` |
| Embeddings (recuperación RAG) | Ollama en `https://your-ollama-host.example` | `nomic-embed-text` |
No se utilizan claves de API pagas. Ningún dato sale de su red. El clúster de 5 GPU de Ollama que ya ejecuta maneja la carga.
@@ -42,12 +42,12 @@ flowchart LR
| `src/pages/api/oracle.ts` | El punto final SSR al que el widget de chat POSTea. También sirve un GET para diagnósticos. |
| `src/components/CosmicHeader.astro` | El lanzador flotante + interfaz de usuario de chat. |
## Configuración única en neuronetz.ai
## Configuración única en su Ollama.ai
Extraiga los dos modelos que usa Oracle:
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
`qwen2.5-coder:14b` ya está instalado (verificado en vivo). `nomic-embed-text` es la pieza faltante; sin ella, el Oracle funciona solo en modo de chat (sin RAG).
@@ -55,9 +55,9 @@ curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
El Oracle lee su configuración desde las variables de entorno. Se incluyen valores predeterminados sensibles.
```bash
# Default mode (Ollama on neuronetz.ai)
# Default mode (Ollama on your Ollama instance)
LLM_PROVIDER=ollama # default
OLLAMA_BASE_URL=https://api.neuronetz.ai # default
OLLAMA_BASE_URL=https://your-ollama-host.example # default
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b # default
OLLAMA_EMBED_MODEL=nomic-embed-text # default
@@ -81,9 +81,9 @@ ORACLE_MAX_TOKENS=800
curl https://nibiru-framework.com/api/oracle
{
"status": "ok",
"llm": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"llm": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "qwen2.5-coder:14b" },
"embed": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"embed": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "nomic-embed-text" },
"index": { "present": true, "chunks": 177,
"provider": "ollama", "model": "nomic-embed-text" }

View File

@@ -1,9 +1,9 @@
---
title: "Desplegando el Sitio de Documentación"
description: "Despliegue de producción de nibiru-framework.com utilizando jwilder/nginx-proxy y tu propio Ollama en neuronetz.ai."
description: "Despliegue de producción de nibiru-framework.com utilizando jwilder/nginx-proxy y tu propio Ollama en su Ollama."
---
Esta página documenta cómo se despliega el sitio de documentación en producción. La configuración utiliza **jwilder/nginx-proxy** para el enrutamiento automático de contenedores Docker, **letsencrypt-nginx-proxy-companion** para HTTPS y **tu propio Ollama en api.neuronetz.ai** para el backend de Oracle — no se requieren claves API de LLM pagadas.
Esta página documenta cómo se despliega el sitio de documentación en producción. La configuración utiliza **jwilder/nginx-proxy** para el enrutamiento automático de contenedores Docker, **letsencrypt-nginx-proxy-companion** para HTTPS y **tu propio Ollama en your-ollama-host.example** para el backend de Oracle — no se requieren claves API de LLM pagadas.
## Topología
```
@@ -17,7 +17,7 @@ Esta página documenta cómo se despliega el sitio de documentación en producci
│ http://nibiru-docs:4321
┌──────────────────────┐ ┌──────────────────────┐
│ nibiru-docs │ ──────▶ │ api.neuronetz.ai
│ nibiru-docs │ ──────▶ │ your-ollama-host.example
│ Astro Node SSR :4321 │ HTTPS │ Ollama (5× GPU) │
│ Oracle endpoint │ │ qwen2.5-coder:14b │
└──────────────────────┘ │ nomic-embed-text │
@@ -31,19 +31,19 @@ docker network create nginx-proxy
# 2) Run nginx-proxy + acme-companion (one time)
# See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.
# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# 3) Pull the Oracle's models on your Ollama instance (one time)
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
## Archivos en `docs/`
| Archivo | Propósito |
|---|---|
| `Dockerfile` | Construcción multi-etapa: construye índice Oracle contra neuronetz.ai, construye Astro, elimina dependencias de desarrollo. |
| `Dockerfile` | Construcción multi-etapa: construye índice Oracle contra your Ollama instance, construye Astro, elimina dependencias de desarrollo. |
| `docker-compose.yml` | Producción — `VIRTUAL_HOST=nibiru-framework.com`, unido a la red `nginx-proxy`. |
| `docker-compose.local.yml` | Anulación de prueba local — expone `4321:4321`, elimina variables de entorno de nginx-proxy. |
| `.dockerignore` | Mantiene `node_modules`, `.git`, etc. fuera del contexto de la construcción. |
| `.env.example` | Plantilla — predeterminado a Ollama en neuronetz.ai, no se requieren claves API. |
| `.env.example` | Plantilla — predeterminado a Ollama en su Ollama, no se requieren claves API. |
## Levántalo
```bash
@@ -81,7 +81,7 @@ La compilación vuelve a ejecutar el índice de Oracle contra el contenido más
| Var | Default | Usado en | Propósito |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | tiempo de ejecución | `ollama` (predeterminado) o `anthropic`. |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | construcción + tiempo de ejecución | Dónde alcanzar a Ollama. |
| `OLLAMA_BASE_URL` | `https://your-ollama-host.example` | construcción + tiempo de ejecución | Dónde alcanzar a Ollama. |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | tiempo de ejecución | Modelo de completación de chat. |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | construcción + tiempo de ejecución | Modelo de incrustación. |
| `EMBED_PROVIDER` | `ollama` | construcción + tiempo de ejecución | `ollama` o `openai`. |
@@ -98,12 +98,12 @@ La compilación vuelve a ejecutar el índice de Oracle contra el contenido más
**Cert no emitido.** Let's Encrypt limita agresivamente las solicitudes. Verifica los registros de `docker logs letsencrypt-nginx-proxy-companion` para determinar la causa.
**Oracle responde sin citas.** El índice de incrustación está vacío. Ya sea que `nomic-embed-text` no se haya extraído en Ollama, o el build no pudo alcanzar neuronetz.ai. Vuelva a extraer el modelo:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
**Oracle responde sin citas.** El índice de incrustación está vacío. Ya sea que `nomic-embed-text` no se haya extraído en Ollama, o el build no pudo alcanzar your Ollama instance. Vuelva a extraer el modelo:```bash
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```
**Oracle devuelve "el Oracle no pudo responder".** Verifica que el modelo de chat esté cargado:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Quieres caer en Claude.** Establece `LLM_PROVIDER=anthropic` y `ANTHROPIC_API_KEY` en `.env`, luego ejecuta `docker compose up -d`.

View File

@@ -5,13 +5,13 @@ description: "Tres formas de iniciar el sitio de documentación (con Oracle) en
El sitio de documentación — y el Oracle que vive en la esquina — es simplemente una aplicación de Astro. Puedes ejecutarlo de tres formas dependiendo de lo que tengas a mano.
## Opción A — Servidor de desarrollo Astro, backend neuronetz.ai (más rápido)
## Opción A — Servidor de desarrollo Astro, backend your Ollama instance (más rápido)
El Oracle llama a tu Ollama compartida en `https://api.neuronetz.ai`. No se necesita un GPU local ni claves de API para gestionar.
El Oracle llama a tu Ollama compartida en `https://your-ollama-host.example`. No se necesita un GPU local ni claves de API para gestionar.
```bash
cd /home/stephan/PhpstormProjects/Nibiru/docs
# .env (copy from .env.example, defaults already point at neuronetz.ai)
# .env (copy from .env.example, defaults already point at your Ollama instance)
cp .env.example .env
npm install # one-time
@@ -21,7 +21,7 @@ Abre <http://localhost:4321/>. Haz clic en el lanzador amarillo de Oracle en la
**Extraiga el modelo de incrustación una vez** en su host Ollama (en tiempo de construcción + en tiempo de ejecución):
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
Sin él, el Oracle sigue funcionando — solo que en modo de chat solamente (sin RAG) y responde basándose en el conocimiento paramétrico del modelo. Con él, las respuestas están respaldadas por esta documentación.
@@ -36,7 +36,7 @@ El servidor de desarrollo lo detectará en la próxima solicitud. O omite este p
El punto final `/api/oracle` del Oracle también responde a GET con su configuración actual (sin secretos):
```bash
curl http://localhost:4321/api/oracle
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://api.neuronetz.ai",
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://your-ollama-host.example",
# "model":"qwen2.5-coder:14b"},"embed":{...},"index":{...}}
```
## Opción B — Docker Compose, localmente
@@ -111,14 +111,14 @@ Si la última llamada devuelve una respuesta real que menciona `./nibiru -m`, tu
**Oracle devuelve "el Oracle no pudo responder".**
El servidor de Ollama no es accesible o el modelo de chat no está extraído. Verifique:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Oracle responde sin citas.**
El índice de incrustación está vacío. Vuelve a ejecutar `npm run build:oracle` después de extraer `nomic-embed-text`.
**Ollama devuelve 404 modelo-no-encontrado.**
Extrae el modelo, por ejemplo:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
```
**`ECONNREFUSED 127.0.0.1:11434`** en la opción C.
El Ollama local no está ejecutándose. Inícielo con `ollama serve &` (o a través de su servicio del sistema).

View File

@@ -65,10 +65,10 @@ Le plugin RAG utilise ce format internalement pour ses fichiers JSON.
## Choix des modèles d'incrustation
Tirez sur neuronetz.ai une fois :
Tirez sur votre Ollama.ai une fois :
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://api.neuronetz.ai/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://your-ollama-host.example/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
```
Dans `ai.ini`:
```ini

View File

@@ -1,11 +1,11 @@
---
title: "Le module IA"
description: "Première classe d'IA dans Nibiru — conversation, embeddings, RAG, agents — connectés à votre propre Ollama sur neuronetz.ai. Aucune API payante requise."
description: "Première classe d'IA dans Nibiru — conversation, embeddings, RAG, agents — connectés à votre propre Ollama sur votre Ollama. Aucune API payante requise."
---
Nibiru embarque un **module IA** (`application/module/ai/`) qui donne à chaque application Nibiru une interface IA de première classe. Le code PHP peut dialoguer avec un LLM local, émbarquer du texte, exécuter RAG sur ses propres données ou exécuter un agent avec des outils — tout cela sans envoyer un seul octet à une API payante.
Le module est connecté par défaut à votre propre [Ollama sur neuronetz.ai](/en/ai/oracle/), donc l'inférence se fait sur votre matériel, sur votre réseau, selon vos termes.
Le module est connecté par défaut à votre propre [Ollama sur votre Ollama](/en/ai/oracle/), donc l'inférence se fait sur votre matériel, sur votre réseau, selon vos termes.
## Ce que vous obtenez
@@ -34,7 +34,7 @@ Voici toute la surface d'API pour le cas simple. Aucun conteneur DI, aucune clé
Chaque plugin lit ses paramètres depuis `application/module/ai/settings/ai.ini`:
```ini
[AI]
ollama.base_url = "https://api.neuronetz.ai"
ollama.base_url = "https://your-ollama-host.example"
chat.model = "nibiru-coder:1.0"
chat.fallback_model = "qwen2.5-coder:14b"
chat.temperature = 0.4

View File

@@ -23,7 +23,7 @@ La personnalisation du système-prompt se fait **instantanément** — pas d'ent
Le script :
1. Lit le fichier Modelfile situé à côté de lui.
2. Envoie une requête POST à `${OLLAMA_BASE_URL}/api/create` (par défaut `https://api.neuronetz.ai`).
2. Envoie une requête POST à `${OLLAMA_BASE_URL}/api/create` (par défaut `https://your-ollama-host.example`).
3. Exécute un test fumeur de conversation pour confirmer que le nouveau tag répond.
Après son succès, définissez le modèle dans `application/module/ai/settings/ai.ini` :

View File

@@ -1,6 +1,6 @@
---
title: "Demandez à l'Oracle"
description: "Comment fonctionne l'assistant IA intégré au site — RAG sur les documents, servi par votre propre Ollama sur neuronetz.ai."
description: "Comment fonctionne l'assistant IA intégré au site — RAG sur les documents, servi par votre propre Ollama sur votre Ollama."
---
Le bouton jaune dans le coin de chaque page est l'**Oracle Nibiru** — un assistant IA basé sur cette documentation même. Posez-lui des questions sur le routage, les modules, la CLI, la couche Smarty, le sens de `pageAction()`. Il cite ses sources.
@@ -11,8 +11,8 @@ Par défaut, l'Oracle fonctionne entièrement sur **votre propre infrastructure*
| Calque | Backend | Modèle par défaut |
|---|---|---|
| Chat (génération de réponse) | Ollama sur `https://api.neuronetz.ai` | `qwen2.5-coder:14b` |
| Embeddings (retrieval RAG) | Ollama sur `https://api.neuronetz.ai` | `nomic-embed-text` |
| Chat (génération de réponse) | Ollama sur `https://your-ollama-host.example` | `qwen2.5-coder:14b` |
| Embeddings (retrieval RAG) | Ollama sur `https://your-ollama-host.example` | `nomic-embed-text` |
Aucune clé d'API payante. Aucune donnée ne quitte votre réseau. Le cluster Ollama à 5 GPU que vous exécutez déjà gère la charge.
@@ -42,12 +42,12 @@ flowchart LR
| `src/pages/api/oracle.ts` | Le point final SSR auquel le widget de conversation POSTe. Il sert également une requête GET pour les diagnostics. |
| `src/components/CosmicHeader.astro` | Le lanceur flottant + l'interface utilisateur de conversation. |
## Configuration unique sur neuronetz.ai
## Configuration unique sur votre Ollama.ai
Extrayez les deux modèles que l'Oracle utilise :
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
`qwen2.5-coder:14b` est déjà installé (vérifié en direct). `nomic-embed-text` est la pièce manquante ; sans elle, l'Oracle fonctionne en mode conversationnel uniquement (sans RAG).
@@ -55,9 +55,9 @@ curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
L'Oracle lit sa configuration à partir des variables d'environnement. Des valeurs par défaut sensibles sont intégrées.
```bash
# Default mode (Ollama on neuronetz.ai)
# Default mode (Ollama on your Ollama instance)
LLM_PROVIDER=ollama # default
OLLAMA_BASE_URL=https://api.neuronetz.ai # default
OLLAMA_BASE_URL=https://your-ollama-host.example # default
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b # default
OLLAMA_EMBED_MODEL=nomic-embed-text # default
@@ -81,9 +81,9 @@ ORACLE_MAX_TOKENS=800
curl https://nibiru-framework.com/api/oracle
{
"status": "ok",
"llm": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"llm": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "qwen2.5-coder:14b" },
"embed": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"embed": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "nomic-embed-text" },
"index": { "present": true, "chunks": 177,
"provider": "ollama", "model": "nomic-embed-text" }

View File

@@ -1,9 +1,9 @@
---
title: "Déploiement du site de documentation"
description: "Déploiement de production de nibiru-framework.com en utilisant jwilder/nginx-proxy et votre propre Ollama sur neuronetz.ai."
description: "Déploiement de production de nibiru-framework.com en utilisant jwilder/nginx-proxy et votre propre Ollama sur votre Ollama."
---
Cette page documente comment le site de documentation est déployé en production. La configuration utilise **jwilder/nginx-proxy** pour le routage automatique des conteneurs Docker, **letsencrypt-nginx-proxy-companion** pour HTTPS et **votre propre Ollama à api.neuronetz.ai** pour le backend Oracle — aucune clé API LLM payante n'est requise.
Cette page documente comment le site de documentation est déployé en production. La configuration utilise **jwilder/nginx-proxy** pour le routage automatique des conteneurs Docker, **letsencrypt-nginx-proxy-companion** pour HTTPS et **votre propre Ollama à your-ollama-host.example** pour le backend Oracle — aucune clé API LLM payante n'est requise.
## Topologie
```
@@ -17,7 +17,7 @@ Cette page documente comment le site de documentation est déployé en productio
│ http://nibiru-docs:4321
┌──────────────────────┐ ┌──────────────────────┐
│ nibiru-docs │ ──────▶ │ api.neuronetz.ai
│ nibiru-docs │ ──────▶ │ your-ollama-host.example
│ Astro Node SSR :4321 │ HTTPS │ Ollama (5× GPU) │
│ Oracle endpoint │ │ qwen2.5-coder:14b │
└──────────────────────┘ │ nomic-embed-text │
@@ -31,19 +31,19 @@ docker network create nginx-proxy
# 2) Run nginx-proxy + acme-companion (one time)
# See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.
# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# 3) Pull the Oracle's models on your Ollama instance (one time)
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
## Fichiers dans `docs/`
| Fichier | Objectif |
|---|---|
| `Dockerfile` | Construction multi-stages : construit l'index Oracle contre neuronetz.ai, construit Astro, élimine les dépendances de développement. |
| `Dockerfile` | Construction multi-stages : construit l'index Oracle contre your Ollama instance, construit Astro, élimine les dépendances de développement. |
| `docker-compose.yml` | Production — `VIRTUAL_HOST=nibiru-framework.com`, joint à réseau `nginx-proxy`. |
| `docker-compose.local.yml` | Remplacement pour test local — expose `4321:4321`, supprime les variables d'environnement nginx-proxy. |
| `.dockerignore` | Garde `node_modules`, `.git`, etc. hors du contexte de construction. |
| `.env.example` | Modèle — par défaut, utilise Ollama sur neuronetz.ai, aucune clé API requise. |
| `.env.example` | Modèle — par défaut, utilise Ollama sur votre Ollama, aucune clé API requise. |
## Levez-le
```bash
@@ -81,7 +81,7 @@ La construction réexécute l'index Oracle sur le contenu le plus récent ; le n
| Var | Default | Utilisé à | Objectif |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | exécution | `ollama` (par défaut) ou `anthropic`. |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | build + exécution | Où joindre Ollama. |
| `OLLAMA_BASE_URL` | `https://your-ollama-host.example` | build + exécution | Où joindre Ollama. |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | exécution | Modèle de complétion conversationnelle. |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | build + exécution | Modèle d'embedding. |
| `EMBED_PROVIDER` | `ollama` | build + exécution | `ollama` ou `openai`. |
@@ -98,12 +98,12 @@ La construction réexécute l'index Oracle sur le contenu le plus récent ; le n
**Le certificat n'a pas été émis.** Let's Encrypt limite agressivement les demandes. Vérifiez `docker logs letsencrypt-nginx-proxy-companion` pour déterminer la cause.
**Oracle répond sans références.** L'index d'incrustation est vide. Soit `nomic-embed-text` n'est pas tiré sur Ollama, soit la construction n'a pas pu atteindre neuronetz.ai. Ré-tirez le modèle :```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
**Oracle répond sans références.** L'index d'incrustation est vide. Soit `nomic-embed-text` n'est pas tiré sur Ollama, soit la construction n'a pas pu atteindre your Ollama instance. Ré-tirez le modèle :```bash
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```
**L'Oracle retourne "l'Oracle ne peut pas répondre".** Vérifiez que le modèle de conversation est chargé :```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Voulez-vous revenir à Claude ?** Définissez `LLM_PROVIDER=anthropic` et `ANTHROPIC_API_KEY` dans le fichier `.env`, puis exécutez `docker compose up -d`.

View File

@@ -5,13 +5,13 @@ description: "Trois façons de déployer le site des documents (avec l'Oracle) s
Le site de documentation — ainsi que l'Oracle qui se trouve dans le coin — est simplement une application Astro. Vous pouvez la lancer de trois façons en fonction de ce que vous avez à portée de main.
## Option A — Serveur de développement Astro, backend neuronetz.ai (le plus rapide)
## Option A — Serveur de développement Astro, backend your Ollama instance (le plus rapide)
L'Oracle appelle votre Ollama partagé à `https://api.neuronetz.ai`. Aucun GPU local nécessaire, aucune clé API à gérer.
L'Oracle appelle votre Ollama partagé à `https://your-ollama-host.example`. Aucun GPU local nécessaire, aucune clé API à gérer.
```bash
cd /home/stephan/PhpstormProjects/Nibiru/docs
# .env (copy from .env.example, defaults already point at neuronetz.ai)
# .env (copy from .env.example, defaults already point at your Ollama instance)
cp .env.example .env
npm install # one-time
@@ -21,7 +21,7 @@ Ouvrez <http://localhost:4321/>. Cliquez sur le lanceur orange d'Oracle en bas
**Tirez le modèle d'incrustation une seule fois** sur votre hôte Ollama (au moment de la construction + au moment de l'exécution) :
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
Sans cela, l'Oracle fonctionne toujours — elle ne fait que s'exécuter en mode conversationnel uniquement (sans RAG) et répond à partir des connaissances paramétriques du modèle. Avec cela, les réponses sont ancrées dans cette documentation.
@@ -36,7 +36,7 @@ Le serveur de développement le détectera lors du prochain appel. Ou bien, igno
Le point d'accès `/api/oracle` de l'Oracle répond également à la méthode GET avec sa configuration actuelle (sans les secrets) :
```bash
curl http://localhost:4321/api/oracle
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://api.neuronetz.ai",
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://your-ollama-host.example",
# "model":"qwen2.5-coder:14b"},"embed":{...},"index":{...}}
```
## Option B — Docker Compose, localement
@@ -111,14 +111,14 @@ Si l'appel final retourne une réponse réelle qui mentionne `./nibiru -m`, votr
**Oracle retourne "le Oracle ne peut pas répondre".**
Le serveur Ollama est inaccessible ou le modèle de conversation n'est pas extrait. Vérifiez :```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Oracle répond sans références.**
L'index d'embedding est vide. Réexécutez `npm run build:oracle` après avoir tiré `nomic-embed-text`.
**Ollama retourne 404 modèle non trouvé.**
Tirez le modèle, par exemple :```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
```
**`ECONNREFUSED 127.0.0.1:11434`** dans l'option C.
Le local Ollama n'est pas en cours d'exécution. Démarrez-le avec `ollama serve &` (ou via votre service système).

View File

@@ -65,10 +65,10 @@ RAG プラグインは、この形式を使用して内部の JSON ファイル
## モデル選択の埋め込み
neuronetz.ai に一度プルしてください。
your Ollama instance に一度プルしてください。
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://api.neuronetz.ai/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}' # 768 dim, default
curl https://your-ollama-host.example/api/pull -d '{"name":"mxbai-embed-large"}' # 1024 dim, higher quality
```
In `ai.ini`:
```ini

View File

@@ -5,7 +5,7 @@ description: "NibiruのファーストクラスAI — チャット、エンベ
Nibiruは**AIモジュール**`application/module/ai/`を搭載しており、これによりNibiruアプリケーションには一級のAIインターフェースが提供されます。PHPコードはローカルのLLMとチャットしたり、テキストを埋め込みたり、自身のデータ上でRAGを実行したり、ツールを使用したエージェントを実行したりすることができ、すべてが支払いが必要なAPIにデータを送信することなくに行われます。
このモジュールはデフォルトであなたの[neuronetz.aiのOllama](/ja/ai/oracle/)に接続されていますので、推論はあなたのハードウェア、ネットワーク、および条件に基づいて行われます。
このモジュールはデフォルトであなたの[your Ollama instanceのOllama](/ja/ai/oracle/)に接続されていますので、推論はあなたのハードウェア、ネットワーク、および条件に基づいて行われます。
## あなたが得る内容
@@ -34,7 +34,7 @@ echo $ai->chat()->ask('How do I scaffold a new module?');
各プラグインは、設定を `application/module/ai/settings/ai.ini` から読みます。
```ini
[AI]
ollama.base_url = "https://api.neuronetz.ai"
ollama.base_url = "https://your-ollama-host.example"
chat.model = "nibiru-coder:1.0"
chat.fallback_model = "qwen2.5-coder:14b"
chat.temperature = 0.4

View File

@@ -23,7 +23,7 @@ description: "ニビル風のチャットモデルを独自のオラマに登録
スクリプト:
1. 同じディレクトリにある Modelfile を読みます。
2. `${OLLAMA_BASE_URL}/api/create`(デフォルトは `https://api.neuronetz.ai`)に POST します。
2. `${OLLAMA_BASE_URL}/api/create`(デフォルトは `https://your-ollama-host.example`)に POST します。
3. 新しいタグが応答することを確認するために、スモークテストのチャットコールを実行します。
成功した後、`application/module/ai/settings/ai.ini` にモデルを設定します。

View File

@@ -11,8 +11,8 @@ description: "サイト内のAIアシスタントの仕組み — ドキュメ
| レイヤー | バックエンド | デフォルトモデル |
|---|---|---|
| チャット(回答生成) | Ollama on `https://api.neuronetz.ai` | `qwen2.5-coder:14b` |
| エンベディングRAG検索 | Ollama on `https://api.neuronetz.ai` | `nomic-embed-text` |
| チャット(回答生成) | Ollama on `https://your-ollama-host.example` | `qwen2.5-coder:14b` |
| エンベディングRAG検索 | Ollama on `https://your-ollama-host.example` | `nomic-embed-text` |
有料のAPIキーはありません。データはあなたのネットワーク内に留まります。既に実行している5-GPUのOllamaクラスターが負荷を処理します。
@@ -46,8 +46,8 @@ flowchart LR
Oracleが使用する2つのモデルを取得します
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
`qwen2.5-coder:14b` は既にインストール済み(確認済み)。`nomic-embed-text` が不足しているこれがないと、Oracle はチャットのみRAGなしモードで動作します。
@@ -55,9 +55,9 @@ curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
Oracleは環境変数から設定を読み込みます。適切なデフォルト値が組み込まれています。
```bash
# Default mode (Ollama on neuronetz.ai)
# Default mode (Ollama on your Ollama instance)
LLM_PROVIDER=ollama # default
OLLAMA_BASE_URL=https://api.neuronetz.ai # default
OLLAMA_BASE_URL=https://your-ollama-host.example # default
OLLAMA_CHAT_MODEL=qwen2.5-coder:14b # default
OLLAMA_EMBED_MODEL=nomic-embed-text # default
@@ -81,9 +81,9 @@ ORACLE_MAX_TOKENS=800
curl https://nibiru-framework.com/api/oracle
{
"status": "ok",
"llm": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"llm": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "qwen2.5-coder:14b" },
"embed": { "provider": "ollama", "ollamaUrl": "https://api.neuronetz.ai",
"embed": { "provider": "ollama", "ollamaUrl": "https://your-ollama-host.example",
"model": "nomic-embed-text" },
"index": { "present": true, "chunks": 177,
"provider": "ollama", "model": "nomic-embed-text" }

View File

@@ -1,9 +1,9 @@
---
title: "ドキュメントサイトのデプロイメント"
description: "nibiru-framework.comの本番デプロイメントは、jwilder/nginx-proxyと独自のOllamaを使用してneuronetz.aiで行います。"
description: "nibiru-framework.comの本番デプロイメントは、jwilder/nginx-proxyと独自のOllamaを使用してyour Ollama instanceで行います。"
---
このページでは、ドキュメントサイトがプロダクションでデプロイされる方法について説明します。セットアップは、**jwilder/nginx-proxy** を使用して自動的に Docker コンテナのルーティングを行うこと、**letsencrypt-nginx-proxy-companion** を使用して HTTPS を提供すること、および **あなたの Ollama at api.neuronetz.ai** を使用して Oracle バックエンドを提供することを含んでいます。有料の LLM API キーは不要です。
このページでは、ドキュメントサイトがプロダクションでデプロイされる方法について説明します。セットアップは、**jwilder/nginx-proxy** を使用して自動的に Docker コンテナのルーティングを行うこと、**letsencrypt-nginx-proxy-companion** を使用して HTTPS を提供すること、および **あなたの Ollama at your-ollama-host.example** を使用して Oracle バックエンドを提供することを含んでいます。有料の LLM API キーは不要です。
## トポロジー
```
@@ -17,7 +17,7 @@ description: "nibiru-framework.comの本番デプロイメントは、jwilder/ng
│ http://nibiru-docs:4321
┌──────────────────────┐ ┌──────────────────────┐
│ nibiru-docs │ ──────▶ │ api.neuronetz.ai
│ nibiru-docs │ ──────▶ │ your-ollama-host.example
│ Astro Node SSR :4321 │ HTTPS │ Ollama (5× GPU) │
│ Oracle endpoint │ │ qwen2.5-coder:14b │
└──────────────────────┘ │ nomic-embed-text │
@@ -31,19 +31,19 @@ docker network create nginx-proxy
# 2) Run nginx-proxy + acme-companion (one time)
# See https://github.com/nginx-proxy/nginx-proxy for the canonical compose.
# 3) Pull the Oracle's models on neuronetz.ai (one time)
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
# 3) Pull the Oracle's models on your Ollama instance (one time)
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
## `docs/` 内のファイル
| ファイル | 機能 |
|---|---|
| `Dockerfile` | 多段階ビルド: neuronetz.ai に対して Oracle インデックスを構築し、Astro を構築し、開発依存関係を削減します。 |
| `Dockerfile` | 多段階ビルド: your Ollama instance に対して Oracle インデックスを構築し、Astro を構築し、開発依存関係を削減します。 |
| `docker-compose.yml` | 生産 — `VIRTUAL_HOST=nibiru-framework.com`、nginx-proxy ネットワークに参加しています。 |
| `docker-compose.local.yml` | ローカルテストオーバーライド — `4321:4321` を公開し、nginx-proxy 環境変数を削除します。 |
| `.dockerignore` | `node_modules``.git` などの構築コンテキストから除外します。 |
| `.env.example` | テンプレート — neuronetz.ai の Ollama をデフォルトにし、API キーは不要です。 |
| `.env.example` | テンプレート — your Ollama instance の Ollama をデフォルトにし、API キーは不要です。 |
## 起動する
```bash
@@ -81,7 +81,7 @@ docker compose up -d --build
| 変数 | デフォルト | 使用場所 | 機能 |
|---|---|---|---|
| `LLM_PROVIDER` | `ollama` | 実行時 | `ollama`(デフォルト)または `anthropic`。 |
| `OLLAMA_BASE_URL` | `https://api.neuronetz.ai` | ビルド + 実行時 | Ollama に到達するための場所。 |
| `OLLAMA_BASE_URL` | `https://your-ollama-host.example` | ビルド + 実行時 | Ollama に到達するための場所。 |
| `OLLAMA_CHAT_MODEL` | `qwen2.5-coder:14b` | 実行時 | チャット完了モデル。 |
| `OLLAMA_EMBED_MODEL` | `nomic-embed-text` | ビルド + 実行時 | 埋め込みモデル。 |
| `EMBED_PROVIDER` | `ollama` | ビルド + 実行時 | `ollama` または `openai`。 |
@@ -98,12 +98,12 @@ docker compose up -d --build
**証明書が発行されていません。** Let's Encrypt は激しくレート制限を行っています。原因については `docker logs letsencrypt-nginx-proxy-companion` を確認してください。
**Oracleは引用せずに回答します。** エンベディングインデックスが空です。`nomic-embed-text`がOllamaで取得されていないか、ビルドがneuronetz.aiに到達できなかった可能性があります。モデルを再度取得してください:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
**Oracleは引用せずに回答します。** エンベディングインデックスが空です。`nomic-embed-text`がOllamaで取得されていないか、ビルドがyour Ollama instanceに到達できなかった可能性があります。モデルを再度取得してください:```bash
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
docker compose up -d --build
```
**Oracle は「Oracle が回答することができませんでした」を返しました。** チャットモデルが取得されていることを確認してください:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Claudeにフォールバックしたい場合** `LLM_PROVIDER=anthropic` と `ANTHROPIC_API_KEY` を `.env` ファイルに設定し、その後 `docker compose up -d` を実行します。

View File

@@ -5,13 +5,13 @@ description: "Oracleでドキュメントサイトを自宅のマシン上で起
ドキュメントサイトとコーナーに住むOracleは、単なるAstroアプリです。手元にあるもの次第で3つの方法で実行できます。
## オプション A — Astro 開発サーバー、neuronetz.ai バックエンド(最速)
## オプション A — Astro 開発サーバー、your Ollama instance バックエンド(最速)
Oracle は、`https://api.neuronetz.ai` の共有 Ollama を呼び出します。ローカルの GPU は必要ありませんし、API キーを管理する必要もありません。
Oracle は、`https://your-ollama-host.example` の共有 Ollama を呼び出します。ローカルの GPU は必要ありませんし、API キーを管理する必要もありません。
```bash
cd /home/stephan/PhpstormProjects/Nibiru/docs
# .env (copy from .env.example, defaults already point at neuronetz.ai)
# .env (copy from .env.example, defaults already point at your Ollama instance)
cp .env.example .env
npm install # one-time
@@ -21,7 +21,7 @@ npm run dev # http://localhost:4321
**Ollamaホスト上で1回だけ埋め込みモデルを取得**(ビルド時+実行時):
```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"nomic-embed-text"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"nomic-embed-text"}'
```
これなしで、Oracleはまだ動作します——それは単にチャットのみno-RAGモードで実行され、モデルのパラメトリックな知識から回答されます。これがあれば、回答はこのドキュメンテーションに基づいています。
@@ -36,7 +36,7 @@ npm run build:oracle # writes public/oracle-index.json
Oracleの`/api/oracle`エンドポイントも、現在の設定シークレットなしでGETリクエストに応答します。
```bash
curl http://localhost:4321/api/oracle
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://api.neuronetz.ai",
# {"status":"ok","llm":{"provider":"ollama","ollamaUrl":"https://your-ollama-host.example",
# "model":"qwen2.5-coder:14b"},"embed":{...},"index":{...}}
```
## オプションB — ローカルでのDocker Compose
@@ -111,14 +111,14 @@ curl -X POST -H 'Content-Type: application/json' \
**Oracle は「Oracle が回答することができませんでした」を返します。**
Ollama サーバーにアクセスできないか、チャットモデルがプルされていない可能性があります。確認してください:```bash
curl https://api.neuronetz.ai/api/tags | jq '.models[].name'
curl https://your-ollama-host.example/api/tags | jq '.models[].name'
```
**Oracle は引用せずに回答します。**
埋め込みインデックスが空です。`nomic-embed-text` をプルした後、`npm run build:oracle` を再実行してください。
**Ollamaが404 model-not-foundを返します。**
モデルを取得してください、例:```bash
curl https://api.neuronetz.ai/api/pull -d '{"name":"qwen2.5-coder:14b"}'
curl https://your-ollama-host.example/api/pull -d '{"name":"qwen2.5-coder:14b"}'
```
**`ECONNREFUSED 127.0.0.1:11434`** オプションCで。
ローカルのOllamaが実行されていません。`ollama serve &`(またはシステムサービス経由で)で開始してください。

View File

@@ -81,7 +81,7 @@ export const POST: APIRoute = async ({ request }) => {
if (lcfg.provider === 'anthropic' && !lcfg.hasAnthropicKey) {
return json({
answer:
'The Oracle is silent — `LLM_PROVIDER=anthropic` is set but `ANTHROPIC_API_KEY` is missing. Set the key, or switch back to `LLM_PROVIDER=ollama` (which is the default and uses your neuronetz.ai instance).',
'The Oracle is silent — `LLM_PROVIDER=anthropic` is set but `ANTHROPIC_API_KEY` is missing. Set the key, or switch back to `LLM_PROVIDER=ollama` (the default).',
sources: [],
});
}
@@ -166,7 +166,9 @@ function json(body: unknown, status = 200): Response {
});
}
// Tiny GET handler so health-check / debugging shows config without secrets.
// Tiny GET handler so health checks can confirm the Oracle is up.
// Intentionally does NOT echo the upstream Ollama URL — that's an internal
// detail. Provider name + model name are fine; the URL stays in env vars.
export const GET: APIRoute = async () => {
const llm = llmConfig();
const emb = embedConfig();
@@ -189,12 +191,10 @@ export const GET: APIRoute = async () => {
status: 'ok',
llm: {
provider: llm.provider,
ollamaUrl: llm.provider === 'ollama' ? llm.ollamaUrl : undefined,
model: llm.provider === 'ollama' ? llm.ollamaChatModel : llm.anthropicModel,
},
embed: {
provider: emb.provider,
ollamaUrl: emb.provider === 'ollama' ? emb.ollamaUrl : undefined,
model: emb.provider === 'ollama' ? emb.ollamaEmbedModel : emb.openaiEmbedModel,
},
index: {