A self-hosted accounting intelligence system built on a tool-calling agentic loop. Numera connects any OpenAI-compatible language model to structured financial data, live web search, and user-uploaded documents. All responses are in French with amounts converted to FCFA, making it directly applicable to businesses operating under UEMOA, CEMAC, and SYSCOHADA jurisdictions.
Zero required runtime dependencies. No framework. No bundler.
- Architecture
- Project Structure
- Features
- Requirements
- Installation
- Configuration
- Provider Setup
- Usage
- UI Capabilities
- API Reference
- Tool Reference
- Data Schema
- Document Support
- Artifact System
- Docker
- Contributing
- License
Numera is a single-process Node.js HTTP server with no framework dependencies. It serves a static browser client and acts as an orchestration layer between the user and a separately-running language model. The LLM is never embedded: any OpenAI-compatible endpoint works.
Browser (public/index.html)
|
| POST /v1/chat/stream Server-Sent Events
|
src/router.js
|
+-- src/routes/chat.js
|
+-- src/agent/stream.js SSE emitter, word-by-word text delivery
|
+-- src/agent/loop.js Agentic loop, up to 10 iterations
|
+-- src/llm/index.js LLMClient (OpenAI-compatible HTTP)
| |
| v
| LLM backend
| Ollama / llama.cpp / LM Studio / OpenAI / Groq / ...
|
+-- src/tools/index.js Tool dispatcher
|
+-- accounting.js 12 tools, reads in-memory JSON
+-- web.js DuckDuckGo search + HTTP fetch
+-- currency.js Fixed BCEAO exchange rates
- The browser sends
POST /v1/chat/streamwith the conversation history. agentLoopprepends the system prompt and callsLLMClient.chat(messages, TOOL_DEFINITIONS).- The LLM responds with one or more
tool_calls. The server executes each synchronously (accounting data, currency) or asynchronously (web, URL fetch) and appendsrole: "tool"messages. - Steps 2–3 repeat until the LLM returns a text response with no
tool_calls(maximum 10 iterations). - The final text is split into ~5-token chunks and streamed to the browser at ~16 ms intervals via SSE.
No streaming from the LLM. All LLM calls are non-streaming (simpler, no partial JSON accumulation). The word-by-word effect comes from chunked SSE delivery of the already-complete response.
No authentication layer. Numera is designed for local or intranet deployment. Add a reverse proxy with auth (nginx, Caddy) if public exposure is required.
Graceful tool failure. If a tool throws, the error is serialized as JSON and returned as the tool result. The LLM decides how to handle it.
numera/
server.js Entry point. Loads data, starts HTTP server.
12 lines — no business logic.
src/
config.js All configuration from environment variables.
Defines 10 provider presets and auto-detection
logic from URL patterns.
data.js Loads and exposes comptabilite.json and
plan_comptable.json as module-level singletons.
router.js HTTP request router. One line per route.
static.js Static file server for public/. Blocks path
traversal outside publicDir.
llm/
index.js LLMClient class. Wraps POST /v1/chat/completions.
Normalises Ollama native-format responses.
Handles provider-specific headers.
agent/
prompt.js System prompt. Defines output language (French),
currency (FCFA), chart/insight/artifact formats,
and tool-use rules. Edit here to change model
behaviour without touching any other file.
loop.js Agentic loop. Manages the tool-call cycle.
Accepts onToolCall/onToolDone hooks for the SSE
emitter.
stream.js SSE handler for POST /v1/chat/stream. Wraps
agentLoop, drives the SSE event sequence.
tools/
index.js Registry. Aggregates DEFINITIONS and LABELS
from all modules. Single execute() dispatcher.
accounting.js 12 synchronous tools reading comptabilite.json.
web.js search_web (DuckDuckGo HTML scraping) and
fetch_url (HTTP/HTTPS with redirect handling).
currency.js convert_to_fcfa using fixed and approximate rates.
documents.js parseDocument: CSV (native), XLSX, DOCX, PDF,
plain text, image (base64).
routes/
chat.js POST /v1/chat/stream
POST /v1/chat/completions
upload.js POST /v1/upload
data.js GET /v1/data /v1/plan-comptable /v1/tools
GET /v1/models /v1/health
public/
index.html Single-page client. Tailwind CSS (CDN),
Chart.js, marked.js. No build step.
data/
comptabilite.json Accounting dataset. Replace to analyse a
different company — no code changes needed.
plan_comptable.json SYSCOHADA chart of accounts.
docker/
Dockerfile Multi-stage Alpine image. Non-root user.
Health check on /v1/health.
docker-compose.yml Three profiles: default (external LLM),
ollama (Ollama local), gpu (Ollama + NVIDIA).
docs/
architecture.md Detailed component diagram, SSE event protocol,
provider compatibility matrix, tool extension guide.
providers.md Step-by-step setup for each provider.
deployment.md Docker commands, health check, data persistence.
.env.example Full variable reference with provider examples.
package.json Optional dependencies: xlsx, mammoth, pdf-parse.
.gitignore
.dockerignore
Conversational accounting analysis Ask any question in French. The model decides which tools to invoke — no keyword routing, no hardcoded responses.
Real-time tool call transparency Each tool invocation appears in a collapsible "Reflexion" panel. The panel updates in real time as SSE events arrive. Clicking the header expands or collapses the step list.
FCFA-native output
All monetary amounts are converted to FCFA using the official BCEAO fixed peg (1 EUR = 655.957 FCFA). Approximate rates are provided for USD, GBP, CHF, and MAD. Exchange rates are defined in src/config.js.
Web search and URL retrieval
The model calls search_web against DuckDuckGo HTML (no API key required) and fetch_url to read specific pages. Used for sector benchmarks, OHADA/SYSCOHADA regulatory lookups, exchange rate updates, and market data.
Document ingestion Upload CSV, Excel, Word, PDF, plain text, or image files directly from the input bar. Documents are parsed server-side and injected as plain text into the conversation context. The model can analyse, compare, or query the uploaded content alongside the accounting records.
Artifact generation
The model produces self-contained documents — HTML reports, Markdown summaries, CSV exports — as <artifact> blocks. Artifacts open in a slide-over panel with download support.
Inline chart rendering
Responses may include <chart> blocks containing Chart.js dataset configurations. The client renders bar, line, pie, and doughnut charts directly in the conversation thread.
Insight cards
<insight> blocks render as colour-coded cards (success / warning / danger / info) with a material icon, surfacing key findings without burying them in prose.
Streaming response Text is delivered in ~5-token chunks every 16 ms. The tool-call phase and the text-generation phase are visually distinct: the Reflexion panel closes automatically when the first text chunk arrives.
Conversation history The browser maintains a rolling history of up to 20 messages sent per request. Context from earlier turns is preserved across multiple questions.
- Node.js >= 18.0.0
- An OpenAI-compatible LLM endpoint with function/tool calling support
| Backend | Tool calling | Port | Notes |
|---|---|---|---|
| DeepSeek | Full support | 3001 | Default; tested with deepseek-v4-flash, deepseek-v4-pro |
| Ollama | Model-dependent | 11434 | Requires llama3.1, qwen2.5, mistral-nemo, or llama3.2 |
| llama.cpp | With --jinja flag |
8080 | Pass --jinja to llama-server; added in build 3842 |
| LM Studio | Model-dependent | 1234 | Enable tool calling in Server > Chat Template settings |
| OpenAI | Full support | remote | gpt-4o, gpt-4-turbo, gpt-4o-mini |
| Groq | Full support | remote | llama-3.1-70b-versatile, mixtral-8x7b |
| Together | Full support | remote | Llama 3.1 and 3.2 variants |
| Fireworks | Full support | remote | llama-v3p1-70b-instruct and others |
git clone https://github.com/yourorg/numera.git
cd numera
# Optional: enables Excel, Word, and PDF parsing
npm installWithout npm install, the server starts normally. CSV, plain text, and image uploads work natively. XLSX, DOCX, and PDF parsing return a descriptive error message if the required package is absent.
All configuration is via environment variables. No config file is parsed at runtime.
cp .env.example .env
# Edit .env to point at your LLM endpoint
node server.js| Variable | Default | Description |
|---|---|---|
PORT |
3002 |
HTTP server port |
LLM_PROVIDER |
auto-detected (see below) | Provider name used for preset lookup and startup log |
LLM_API_URL |
http://localhost:3001/v1 |
Full base URL including /v1 |
LLM_API_KEY |
(empty) | Bearer token. Leave empty for local/unauthenticated servers |
LLM_MODEL |
deepseek-v4-flash |
Model identifier sent verbatim in every request |
LLM_TEMPERATURE |
0.2 |
Sampling temperature (0.0–1.0). Lower = more deterministic |
LLM_MAX_TOKENS |
4096 |
Maximum tokens per LLM response |
LLM_TIMEOUT |
120000 |
LLM request timeout in milliseconds |
When LLM_PROVIDER is not set, the provider is inferred from LLM_API_URL:
| URL pattern | Detected provider |
|---|---|
openai.com |
openai |
anthropic.com |
anthropic |
groq.com |
groq |
together.xyz |
together |
fireworks.ai |
fireworks |
:11434 |
ollama |
:8080 |
llamacpp |
:1234 |
lmstudio |
:3001 |
deepseek |
| (anything else) | custom |
Detection only affects the startup log and the default model. It does not change how requests are made.
# No env vars needed if the server runs on localhost:3001
node server.jsollama pull llama3.1 # or: qwen2.5:7b mistral-nemo llama3.2
export LLM_PROVIDER=ollama
export LLM_API_URL=http://localhost:11434/v1
export LLM_MODEL=llama3.1
node server.jsOllama has supported POST /v1/chat/completions since v0.1.24. Tool calling works with models that include a tool-capable chat template. Confirmed: llama3.1, qwen2.5, mistral-nemo, llama3.2.
./llama-server \
--model path/to/model.gguf \
--jinja \
--port 8080 \
--ctx-size 8192
export LLM_PROVIDER=llamacpp
export LLM_API_URL=http://localhost:8080/v1
export LLM_MODEL=local
node server.js--jinja enables the Jinja2 chat template engine required for tool calling. Added in build 3842 (November 2024). Recommended models: Llama 3.1 8B/70B GGUF, Qwen 2.5 7B GGUF.
# In LM Studio: Server > Chat Template > enable tool calling
export LLM_PROVIDER=lmstudio
export LLM_API_URL=http://localhost:1234/v1
export LLM_MODEL=local
node server.jsexport LLM_PROVIDER=openai
export LLM_API_URL=https://api.openai.com/v1
export LLM_API_KEY=sk-...
export LLM_MODEL=gpt-4o
node server.jsexport LLM_PROVIDER=groq
export LLM_API_URL=https://api.groq.com/openai/v1
export LLM_API_KEY=gsk_...
export LLM_MODEL=llama-3.1-70b-versatile
node server.jsFree tier: 14,400 requests/day, 6,000 tokens/min. Tool calling works on all available models.
export LLM_PROVIDER=together
export LLM_API_URL=https://api.together.xyz/v1
export LLM_API_KEY=...
export LLM_MODEL=meta-llama/Llama-3.1-8B-Instruct-Turbo
node server.jsexport LLM_PROVIDER=fireworks
export LLM_API_URL=https://api.fireworks.ai/inference/v1
export LLM_API_KEY=fw_...
export LLM_MODEL=accounts/fireworks/models/llama-v3p1-70b-instruct
node server.jsnode server.js
# or
npm startOpen http://localhost:3002. The welcome screen displays six quick-action buttons and a text input. Press Enter or click the send button to submit a message.
Example queries
Montre le bilan complet avec graphiques
Analyse la tresorerie mensuelle et identifie les tensions de liquidite
Compare nos ratios aux benchmarks du secteur IT en Afrique de l Ouest
Genere un rapport HTML complet du compte de resultat
Exporte la liste des clients et encours en CSV
Quel est l impact d une reduction du delai client de 68 a 45 jours sur le BFR ?
Cherche les normes SYSCOHADA applicables aux immobilisations incorporelles
Attaching a document
Click the paperclip icon in the input bar. The selected file is uploaded to POST /v1/upload, parsed server-side, and appended as structured context to the next message sent. Chip indicators appear above the input showing upload status. Chips are cleared after the message is sent.
Viewing artifacts
When the model produces a report or export, a card with an "Ouvrir" button appears in the conversation. Clicking it opens the artifact in a slide-over panel. The "Telecharger" button saves the file locally.
The single-page client (public/index.html) requires no build step. It loads Tailwind CSS, Chart.js, and marked.js from CDN.
Each response begins with a collapsible "Reflexion" section. It lists each tool call in order with a status indicator:
- Spinning icon while the tool is executing
- Green check when the tool completes
The panel header updates to show the total number of sources consulted and collapses automatically when the first text token arrives. Click the header at any time to expand or collapse.
Artifacts open in a right-side slide-over with three rendering modes:
- HTML: rendered in a sandboxed
<iframe> - Markdown: rendered with marked.js in a scrollable
.prosecontainer - CSV: rendered as a sticky-header scrollable table
Clicking the backdrop or the close button dismisses the panel.
Supported via the paperclip button. The browser reads the file, base64-encodes it, and posts JSON to /v1/upload. A chip badge shows the filename and upload result. Multiple files can be attached before sending.
<chart> blocks in LLM responses are extracted before markdown parsing. Each block contains a JSON Chart.js dataset configuration. The client instantiates new Chart(canvas, { type, data, options }) 100 ms after the message is finalised. Supported types: bar, line, pie, doughnut.
During streaming, the assistant message renders as escaped plain text with a blinking cursor. When the done SSE event arrives, the full text is re-rendered through the complete pipeline: special-tag extraction, markdown parsing, placeholder substitution, chart initialisation.
Primary endpoint. Returns a text/event-stream response.
Request
{
"model": "numera-pro",
"messages": [
{ "role": "user", "content": "Analyse le bilan" }
]
}The model field is accepted but ignored server-side. The active model is configured via LLM_MODEL.
SSE event sequence
data: {"type":"start"}
data: {"type":"tool_call","name":"get_bilan","label":"Bilan comptable"}
data: {"type":"tool_done","name":"get_bilan","label":"Bilan comptable"}
data: {"type":"text_delta","content":"Voici le bilan de "}
data: {"type":"text_delta","content":"TechInnov SARL "}
data: {"type":"done","tool_count":1}
Event schema
| type | fields | description |
|---|---|---|
start |
— | Agent loop has started |
tool_call |
name, label |
Tool invocation in progress |
tool_done |
name, label |
Tool execution complete |
text_delta |
content |
~5-token text fragment delivered every ~16 ms |
done |
tool_count |
All text delivered, stream complete |
error |
message |
Fatal error; message contains markdown explanation |
Client implementation note
The endpoint requires POST, so EventSource cannot be used. The browser client reads the stream with response.body.getReader() and splits on \n to parse SSE lines.
OpenAI-compatible synchronous endpoint. Runs the full agentic loop and returns a standard completion object. Suitable for non-streaming integrations, scripting, and testing.
curl -s -X POST http://localhost:3002/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"CA total ?"}]}' \
| jq .choices[0].message.contentParse a document server-side and return structured data.
Request body
{
"filename": "factures_q3.csv",
"mimeType": "text/csv",
"content": "<base64-encoded file bytes>"
}All fields are required. mimeType may be an empty string; format detection falls back to the file extension.
Response — CSV
{
"type": "csv",
"filename": "factures_q3.csv",
"headers": ["date", "client", "montant_ht", "tva"],
"rows": [{ "date": "2025-07-01", "client": "Acme Corp", "montant_ht": "450000", "tva": "81000" }],
"row_count": 312
}Response — Excel
{
"type": "xlsx",
"filename": "budget_2025.xlsx",
"sheet_names": ["Produits", "Charges"],
"sheets": {
"Produits": [{ "Mois": "Janvier", "Budget": 65000, "Reel": 71200 }]
}
}Response — PDF / DOCX
{
"type": "pdf",
"filename": "rapport_audit.pdf",
"pages": 14,
"text": "Rapport d audit interne — Exercice 2025 ..."
}Response — Image
{
"type": "image",
"filename": "facture_scan.jpg",
"mimeType": "image/jpeg",
"base64": "...",
"size_kb": 284
}Returns server status. Used by Docker health checks and monitoring systems.
{
"status": "ok",
"version": "2.0.0",
"provider": "deepseek",
"model": "deepseek-v4-flash",
"data": "loaded",
"uptime": 3842
}data is either "loaded" or "not_loaded". The server will not accept chat requests usefully if data is "not_loaded".
Returns the full parsed comptabilite.json as JSON. Useful for inspecting the active dataset or building external integrations.
Returns the SYSCOHADA chart of accounts from plan_comptable.json.
Returns the complete tool definition array as sent to the LLM. Useful for debugging tool schemas and verifying that newly added tools are registered correctly.
curl -s http://localhost:3002/v1/tools | jq '[.tools[].function.name]'Returns a minimal OpenAI-compatible model list with the active model identifier.
The model has access to 15 tools, all visible in the Reflexion panel during execution.
All 12 tools are synchronous and read from the in-memory accounting dataset. They return raw JSON; the model formats and converts values as instructed by the system prompt.
| Tool | Arguments | Returns |
|---|---|---|
get_company_info |
— | Name, SIRET, legal form, capital, director, fiscal year |
get_bilan |
section? actif/passif/complet |
Balance sheet section(s) |
get_compte_resultat |
— | Full P&L: products, charges, net result |
get_tresorerie |
— | Monthly cash flows + net cash position |
get_ratios |
— | Gross margin, EBITDA, CAF, FR, BFR, DSO, DPO, etc. |
get_clients |
— | Top clients with revenue and outstanding receivables |
get_charges_personnel |
— | Gross salaries by position, employer contributions |
get_immobilisations |
— | Fixed assets: cost, depreciation schedule, NBV |
get_budget_vs_reel |
— | Budget vs actual with absolute and % variance |
get_tva |
— | Deductible VAT and VAT payable |
get_grand_livre |
compte? prefix string |
General ledger balances, optionally filtered |
get_journal_entries |
journal? code, limit? |
Journal entries (VT=sales, HA=purchases, BQ=bank, OD=misc) |
| Tool | Arguments | Returns |
|---|---|---|
search_web |
query (string), max_results? (1–10) |
{ query, results: [{ rank, url, title, snippet }] } |
fetch_url |
url (string) |
{ url, title, text, status, length } or { error } |
search_web scrapes DuckDuckGo HTML (html.duckduckgo.com). No API key. Timeout: 12 s.
fetch_url makes a direct HTTP/HTTPS GET, strips HTML tags, and caps output at 10,000 characters. Follows up to 5 redirects. Does not execute JavaScript. Timeout: 15 s.
| Tool | Arguments | Returns |
|---|---|---|
convert_to_fcfa |
amount (number), from_currency? (EUR/USD/GBP/CHF/MAD) |
{ montant_original, devise_source, montant_fcfa, taux_applique, formatted, note } |
The EUR/XOF rate (655.957) is the official BCEAO fixed peg. Rates for other currencies are approximations stored in src/config.js → exchange.
- Choose or create a module in
src/tools/. - Add the tool definition to
DEFINITIONSusing the OpenAI function schema format. - Add a label to
LABELS. - Add the tool name to
handles()(or create the function if it is a new module). - Add execution logic to
execute(). The function may beasync. - If it is a new module, import and register it in
src/tools/index.js.
Tool descriptions are the primary signal the LLM uses to decide whether and when to call a tool. Write descriptions that are specific about what data the tool returns and in what situations it should be used.
The accounting dataset (data/comptabilite.json) follows a structure loosely aligned with SYSCOHADA conventions. Replace this file to analyse a different company. No code changes are required.
| Key | Type | Description |
|---|---|---|
entreprise |
string | Company name |
siret |
string | SIRET registration number |
forme_juridique |
string | Legal form (SARL, SA, SAS, etc.) |
capital_social |
number | Share capital in EUR |
date_ouverture |
string | Fiscal year start date (YYYY-MM-DD) |
date_cloture |
string | Fiscal year end date (YYYY-MM-DD) |
dirigeant |
string | Director name |
expert_comptable |
string | Accountant name or firm |
bilan_actif |
object | Balance sheet — assets (immobilisé, circulant) |
bilan_passif |
object | Balance sheet — equity, provisions, debt |
compte_resultat |
object | P&L — products and charges by category |
ratios |
object | Pre-computed financial ratios |
tresorerie_mensuelle |
array | 12 monthly entries: { mois, entrees, sorties, solde } |
clients_principaux |
array | Top clients: { nom, ca, secteur, encours } |
immobilisations |
array | Fixed assets: { designation, date_acquisition, valeur_acquisition, duree_amortissement, amortissement_annuel, vnc } |
budget_vs_reel |
object | { produits, charges, resultat } each with { budget, reel, ecart, ecart_pourcentage } |
ecritures_journal |
array | Journal entries: { date, piece, libelle, compte_debit, compte_credit, montant, journal } |
grand_livre |
object | Keyed by account number: { nom, solde_debut, mouvements_debit, mouvements_credit, solde_fin } |
# Replace the file and restart the server
cp new_comptabilite.json data/comptabilite.json
node server.jsdata.js also exports reloadData(dataDir), which can be called programmatically without restarting the process.
Documents are uploaded via POST /v1/upload and parsed server-side. The extracted content is appended as plain text to the next chat message.
| Format | Extensions | Requires | Behaviour |
|---|---|---|---|
| CSV | .csv |
none | Auto-detects , or ;. First 200 rows returned as JSON array. |
| Excel | .xlsx, .xls |
xlsx |
All sheets returned as arrays of objects. |
| Word | .docx |
mammoth |
Raw text extracted, no formatting preserved. |
.pdf |
pdf-parse |
Text-layer PDFs only. Scanned PDFs are not supported without OCR. | |
| Plain text | .txt, .md, .log, .rst |
none | First 50,000 characters returned. |
| Image | .png, .jpg, .jpeg, .webp, .gif, .bmp |
none | Returned as base64 with MIME type. Analysed by vision-capable models. |
If a required package is not installed, the endpoint returns a JSON error with the install command. All other formats continue to work.
npm install # installs all three packages
npm install xlsx # Excel only
npm install mammoth # Word only
npm install pdf-parse # PDF onlyThe model produces artifacts when a standalone document is more appropriate than inline text. Artifacts are embedded in the response as <artifact> XML blocks, extracted before markdown parsing, and rendered in the slide-over panel.
HTML (type="html")
A complete HTML document with inline CSS. Rendered in a sandboxed <iframe> (sandbox attribute: allow-scripts allow-same-origin). The model uses a neutral colour palette: background #faf9f7, text #1a1c1b, accent #3b82f6.
Markdown (type="markdown")
A structured document rendered with marked.js. Suitable for content that will be copied into external tools, email, or reporting systems.
CSV (type="csv")
Tabular data rendered as a scrollable table with sticky column headers. Downloaded as a .csv file.
The system prompt instructs the model to produce an artifact when the user requests a report, export, or shareable document. Effective phrasings:
Genere un rapport HTML complet du bilan
Exporte les immobilisations en CSV
Produis un document Markdown resumant l exercice 2025
Cree un tableau de bord HTML autonome avec graphiques integres
<artifact type="html" title="Rapport Bilan 2025">
<!DOCTYPE html>
<html lang="fr">
...
</html>
</artifact>
The client extracts <artifact> blocks using a regex before passing the response to marked.parse. Extracted blocks are stored in a module-level artifactRegistry object keyed by a generated ID. The registry is referenced when the user clicks "Ouvrir".
The Dockerfile produces a multi-stage Alpine image (~120 MB). The application runs as a non-root user (numera, uid 1001). The data/ directory should be mounted as a volume.
docker build -f docker/Dockerfile -t numera .
docker run -d \
--name numera \
-p 3002:3002 \
-e LLM_API_URL=http://host.docker.internal:3001/v1 \
-e LLM_MODEL=deepseek-v4-flash \
-v "$(pwd)/data":/app/data:ro \
--restart unless-stopped \
numerahost.docker.internal resolves to the host machine on Docker Desktop (macOS, Windows) and when extra_hosts: host-gateway is set (Linux, as in the compose file).
cp .env.example .env
# Set LLM_API_URL, LLM_API_KEY, LLM_MODEL in .env
docker compose up -d
docker compose logs -f numeradocker compose --profile ollama up -d
docker exec ollama ollama pull llama3.1
# Numera becomes available at http://localhost:3002The ollama service has a health check on ollama list. The numera-ollama service starts only after Ollama passes the health check.
# Host requirement: nvidia-container-toolkit installed and configured
docker compose --profile gpu up -d
docker exec ollama ollama pull llama3.1curl http://localhost:3002/v1/health
# {"status":"ok","version":"2.0.0","provider":"ollama","model":"llama3.1","data":"loaded","uptime":120}Include:
- The exact question or action that triggered the problem
- The full server startup output (
node server.jsoutput before the first request) - LLM provider, model, and version
- Node.js version (
node --version)
Any endpoint implementing POST /v1/chat/completions per the OpenAI specification works without code changes. To register a named preset:
- Add an entry to the
PROVIDERSmap insrc/config.jswithbaseUrl,model, andrequiresKey. - Add a URL pattern match in
detectProvider()if auto-detection from URL is desired. - Add a setup section to
docs/providers.md.
src/agent/prompt.js is the single point of control for model behaviour: output language, output currency, chart format, insight format, artifact format, and tool-use rules. Changes take effect after server restart. The file is intentionally isolated from all other modules so it can be edited without risk of breaking application logic.
Current enforced behaviours:
- French as the output language
- FCFA as the mandatory output currency with explicit conversion instruction
- Prohibition on fabricated numbers
<chart>,<insight>, and<artifact>output formats
Extend parseDocument() in src/tools/documents.js. The function signature is async parseDocument(filename, mimeType, buffer). Return a plain JSON-serialisable object. The object is serialised and appended verbatim to the conversation context, so keep it concise and structured.
Replace data/comptabilite.json with a file matching the schema documented in the Data Schema section. All 12 accounting tools read exclusively from this file. No code changes are required.
docs/architecture.md— detailed component diagram, full SSE event protocol, provider compatibility matrix, tool extension walkthroughdocs/providers.md— step-by-step setup instructions for each supported providerdocs/deployment.md— Docker commands, health check integration, data persistence patterns.env.example— annotated variable reference with complete provider configuration examples
MIT. See LICENSE.