Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ Redaction reduces accidental exposure but is not a compliance certification. Rev
| [`@jcode.labs/ragmir-tts`](./packages/ragmir-tts/README.md) | Optional local audio or explicit online voice rendering |

Installing Core does not install Chat or TTS. Add only the optional package needed by the workflow.
Choose Chat with `--profile lite`, `fast`, or `quality`; choose offline TTS with `--lang en`, `fr`,
or `es`. The package guides explain the model sizes, preload step, and online boundaries.

## Runnable examples

Expand Down
3 changes: 2 additions & 1 deletion context7.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"Use one `createRagmirClient()` per project root for repeated work in a stateful Node.js process; close it during shutdown and use the top-level functions for one-shot scripts.",
"Ragmir does not open an HTTP port; a network-facing host owns its transport, authentication, authorization, and rate limits.",
"`rgr chat` is optional add-on generation via @jcode.labs/ragmir-chat; the core remains retrieval-only.",
"Qwen and Gemma are optional Ragmir Chat profiles, never requirements of Core, the CLI, the TypeScript API, or MCP.",
"@jcode.labs/ragmir-chat exposes lite (Qwen2.5 0.5B), default fast (Gemma 4 E2B), and explicit quality (Gemma 4 E4B) profiles; setup, doctor, and generation should use the same profile, and none is required by Core or MCP.",
"@jcode.labs/ragmir-tts selects a language-specific offline MMS model for en, fr, or es; explicit online Edge mode also supports ja, th, and zh, and its default voice follows the selected language.",
"The `local-hash` embedding provider (default) is a lexical sha256 embedding, not semantic; use `transformers` for semantic retrieval.",
"Switching `embeddingProvider` requires `rgr ingest --rebuild`, since the two providers produce incompatible vectors.",
"Ingestion commits resumable file batches under ignored `.ragmir/storage/` state; use `rgr status --json` for progress, and expect `--rebuild` to preserve the active index until a staged generation is validated.",
Expand Down
8 changes: 8 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ const result = await generateChatAnswer({
console.log(result.answer, result.citationStatus)
```

`profile` accepts `lite` (Qwen2.5 0.5B, ~0.49 GB, thinking off), `fast` (default Gemma 4 E2B,
~3.35 GB), or `quality` (Gemma 4 E4B, ~5.15 GB). Setup, doctor, and generation should use the same
profile.

### Recommended Chat exports

| Export | Purpose |
Expand Down Expand Up @@ -338,6 +342,10 @@ first call explicitly preloads the local model from non-sensitive text. Later ca
`allowRemoteModels: false` for confidential content. The Edge path is explicit and sends narration
text to the external service.

The local Transformers.js path supports `language: "en"`, `"fr"`, and `"es"`, each with its own
automatically selected MMS model. Edge additionally supports `"ja"`, `"th"`, and `"zh"`. French is
the default when no language is provided.

`RenderSpeechOptions.signal` stops before subsequent render or write phases. The Edge CLI is also
terminated when cancelled and defaults to a 120-second bound; override it with `edgeTimeoutMs` when
the host needs a shorter deadline.
Expand Down
7 changes: 7 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ rgr audio /tmp/ragmir-tts-preload.txt --lang en --allow-remote-models --out .rag
rgr audio ./brief.md --lang en --offline --out .ragmir/audio/brief.wav
```

Keep the same Chat profile across `setup`, `doctor`, and answers: `lite` is the ~0.49 GB Qwen option,
`fast` is the default ~3.35 GB Gemma option, and `quality` is the explicit ~5.15 GB Gemma option.
For offline TTS, keep the same `--lang` across preload and render: `en`, `fr`, and `es` select their
own local model automatically. Edge additionally supports `ja`, `th`, and `zh` when explicitly
selected.

| Command | Purpose |
| --- | --- |
| `models pull [--enable]` | Preload the configured embedding model and optionally enable semantic retrieval. |
Expand All @@ -109,6 +115,7 @@ OCR runs only for PDF pages without embedded text. The strict privacy profile di
extractors. The first audio command above explicitly downloads the model from non-sensitive text;
the second uses the prepared cache and does not download anything. See the
[offline TTS guide](./offline-tts-preload.md) for model paths and verification.
See [offline Chat](./offline-chat-preload.md) for profile selection and air-gapped preparation.

## Agents, maintenance, and JSON

Expand Down
20 changes: 12 additions & 8 deletions docs/offline-chat-preload.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ rgr chat doctor --profile fast --verify
rgr chat "What evidence supports this decision?" --profile fast --offline
```

| Profile | Use |
| --- | --- |
| `lite` | Smaller Qwen2.5 profile for lower-memory machines. |
| `fast` | Default Gemma 4 profile for a stronger local answer. |
| `quality` | Larger Gemma 4 profile, explicit opt-in. |
| Profile | Pinned model | Download | Thinking | Use |
| --- | --- | --- | --- | --- |
| `lite` | Qwen2.5 0.5B Q4_K_M | ~0.49 GB | Off | Lower-memory machines and short answers. |
| `fast` | Gemma 4 E2B Q4_0 | ~3.35 GB | Standard or deep | Balanced default. |
| `quality` | Gemma 4 E4B Q4_0 | ~5.15 GB | Standard or deep | Larger explicit quality option. |

Setup downloads and verifies the selected GGUF under `.ragmir/models/chat/<profile>`. Normal answers use that local file and do not download or build a runtime.
Use the same profile in all three commands. Setup downloads and verifies the selected GGUF under
`.ragmir/models/chat/<profile>`. Normal answers use that local file and do not download or build a
runtime. `lite` forces `--thinking off`; `fast` is the default when no profile is provided.

These are Chat implementation profiles, not Ragmir Core requirements. You can use Core with your
preferred AI, a local model runner, a deterministic application, or no generative model at all.

## Air-gapped use

Prepare and verify the profile on a connected machine, copy the complete profile directory into the same ignored local path, then rerun `rgr chat doctor --verify` before using `--offline`.
Prepare and verify the profile on a connected machine, copy the complete profile directory into the
same ignored local path, then rerun `rgr chat doctor --profile <profile> --verify` before using
`--offline`.

## Operational rules

- The runtime selects its packaged acceleration backend automatically.
- The runtime selects Metal, CUDA, Vulkan, or CPU from its packaged backends automatically.
- Visible answers retain citations; raw model thought is never shown, logged, or stored.
- Use an external agent only when sending retrieved passages to that agent is acceptable for the corpus.
23 changes: 19 additions & 4 deletions docs/offline-tts-preload.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
# Offline TTS

Ragmir renders local WAV audio through Transformers.js. Download the model once with non-sensitive text, then render confidential content offline.
Ragmir renders local WAV audio through Transformers.js. Download the language model once with
non-sensitive text, then render confidential content offline.

```bash
printf '%s\n' "Non-sensitive model preload text." > /tmp/ragmir-tts-preload.txt
rgr audio /tmp/ragmir-tts-preload.txt --lang en --allow-remote-models --out .ragmir/audio/preload.wav
rgr audio ./brief.md --lang en --offline --out .ragmir/audio/brief.wav
```

The first command warms `.ragmir/models/tts`. The second command requires the local model and does not download anything. Generated files stay in ignored `.ragmir/audio/` state.
The first command warms `.ragmir/models/tts`. The second command requires the local model and does
not download anything. Generated files stay in ignored `.ragmir/audio/` state.

Use the same language code for both commands:

| `--lang` | Language | Offline model |
| --- | --- | --- |
| `en` | English | `Xenova/mms-tts-eng` |
| `fr` | French | `Xenova/mms-tts-fra` |
| `es` | Spanish | `Xenova/mms-tts-spa` |

French is the default when `--lang` is omitted. Run `rgr audio --doctor --json` to inspect the
supported offline and Edge language lists.

For an online MP3 voice, choose Edge explicitly:

```bash
rgr audio ./brief.md --engine edge --out .ragmir/audio/brief.mp3
rgr audio ./brief.md --engine edge --lang ja --out .ragmir/audio/brief.mp3
```

Use that path only when sending the narration text to the external service is acceptable.
Edge also supports `en`, `fr`, `es`, `ja`, `th`, and `zh`; its default voice follows `--lang` unless
`--voice` overrides it. Use that path only when sending the narration text to the external service
is acceptable.
9 changes: 5 additions & 4 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ Public subtagline: **Stop sending confidential documents directly to the cloud.*
authorization belong to the embedding application.
- Default retrieval: local-hash, with no model download. Semantic embeddings and local GGUF chat are
explicit opt-ins.
- Optional packages: `@jcode.labs/ragmir-chat` for local cited GGUF synthesis and
`@jcode.labs/ragmir-tts` for local audio or explicitly selected online speech.
- Model boundary: Qwen and Gemma are optional Chat profile choices. Ragmir Core, its CLI, API, and
MCP server do not depend on either family or on any generative model.
- Optional packages: `@jcode.labs/ragmir-chat` offers `lite` Qwen, default `fast` Gemma, and explicit
`quality` Gemma profiles; `@jcode.labs/ragmir-tts` supports offline `en`, `fr`, and `es`, plus
explicit online Edge voices for `ja`, `th`, and `zh`.
- Model boundary: Chat profiles and TTS language models are optional. Ragmir Core, its CLI, API, and
MCP server do not depend on any generative model.
- Privacy boundary: the corpus and index stay local. A cloud agent receives retrieved passages only
when the user chooses that workflow, under that provider's data policy.
- OCR boundary: embedded PDF text comes first; a configured local executable runs only for blank
Expand Down
18 changes: 10 additions & 8 deletions packages/ragmir-chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ npx rgr chat "What evidence supports this decision?" --profile fast --offline
`rgr chat setup` downloads and verifies one model under `.ragmir/models/chat/<profile>`. Normal
generation uses that local file and rejects remote model resolution.

| Profile | Intended use |
| --- | --- |
| `lite` | Smaller Qwen2.5 profile for lower-memory machines |
| `fast` | Default Gemma 4 profile for balanced local use |
| `quality` | Larger Gemma 4 profile, enabled explicitly |

These are Chat profiles, not requirements of Ragmir Core, the CLI, API, or MCP server. Verify a
prepared profile with `npx rgr chat doctor --profile fast --verify` before offline use.
| Profile | Pinned model | Download | Thinking | Choose it when |
| --- | --- | --- | --- | --- |
| `lite` | Qwen2.5 0.5B Q4_K_M | ~0.49 GB | Off | Memory and startup time matter most |
| `fast` | Gemma 4 E2B Q4_0 | ~3.35 GB | Standard or deep | You want the balanced default |
| `quality` | Gemma 4 E4B Q4_0 | ~5.15 GB | Standard or deep | You accept a larger model for stronger answers |

Use the same profile for `setup`, `doctor`, and each answer. The `fast` profile is the default;
`lite` always disables thinking. These are Chat profiles, not requirements of Ragmir Core, the CLI,
API, or MCP server. Verify a prepared profile with
`npx rgr chat doctor --profile fast --verify` before offline use.

## TypeScript API

Expand Down
9 changes: 6 additions & 3 deletions packages/ragmir-core/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1209,16 +1209,19 @@ program

program
.command("audio")
.description("Render a narration text file to local speech audio with Ragmir TTS.")
.description("Render narration as offline WAV or explicitly selected online Edge MP3.")
.argument("[text-file]", "Narration text file to render.")
.option("-o, --out <path>", "Output MP3 or WAV path.")
.option("--engine <engine>", "TTS engine: auto, edge, or transformers.")
.option("--lang <language>", "TTS language: en, es, fr, ja, th, or zh.")
.option(
"--lang <language>",
"TTS language. Offline: en, es, fr. Edge also: ja, th, zh. Default: fr.",
)
.option("--model <id>", "Transformers.js TTS model ID.")
.option("--model-path <path>", "Local model/cache path.")
.option("--offline", "Force the Transformers.js local/offline WAV path.")
.option("--allow-remote-models", "Explicitly allow remote model downloads.")
.option("--voice <voice>", "Edge voice. Defaults to fr-FR-DeniseNeural.")
.option("--voice <voice>", "Edge voice override. Default matches --lang.")
.option("--rate <rate>", "Edge rate. Defaults to +0%.")
.option("--speaker-embeddings <path>", "Optional model-specific speaker embedding path or URL.")
.option("--speed <number>", "Optional model-specific speech speed.", parseNumber)
Expand Down
2 changes: 1 addition & 1 deletion packages/ragmir-core/src/mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ describe("MCP protocol contract", () => {

const usage = await jsonToolResult(client, "ragmir_usage_report", { days: 1 })
expect(usage).toMatchObject({ totalEvents: expect.any(Number) })
})
}, 15_000)

it("should bound audit, evaluation, and resource payloads", async () => {
const root = await createProject("ragmir-mcp-budget-", {
Expand Down
2 changes: 1 addition & 1 deletion packages/ragmir-core/src/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe("setupProject", () => {
expect(config.embeddingProvider).toBe("transformers")
expect(config.embeddingModelPath).toBe(".ragmir/models")
expect(config.transformersAllowRemoteModels).toBe(false)
})
}, 15_000)

it("auto-ingests supported files when the privacy posture is clean", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "ragmir-setup-"))
Expand Down
6 changes: 4 additions & 2 deletions packages/ragmir-landing/public/ai.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ confidential documents directly to the cloud." only as its secondary public subt
store or RAG service.
- A cloud agent can receive the retrieved passages when the user chooses that workflow. Do not claim
the complete workflow stays local unless the consumer is also local.
- Ragmir Chat is an optional path for local answer generation. Qwen and Gemma are Chat profile
choices, not Core, CLI, API, or MCP requirements.
- Ragmir Chat is an optional path for local answer generation: `lite` uses Qwen, while default
`fast` and explicit `quality` use Gemma. These profiles are not Core, CLI, API, or MCP requirements.
- Ragmir TTS supports offline English, French, and Spanish. Japanese, Thai, and Chinese require the
explicitly selected online Edge path unless the operator supplies a compatible local model.
- Ragmir has no built-in cloud sync. For team use, describe one externally synchronized source-of-
truth folder, an aligned Ragmir version, configuration, embedding provider, and model, and a
separate local ingest on each workstation. Mention that missing, partial, or extra source files
Expand Down
8 changes: 5 additions & 3 deletions packages/ragmir-landing/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ Public subtagline: **Stop sending confidential documents directly to the cloud.*
limits belong to the embedding application.
- The default `local-hash` path works offline with no account, API key, or model download.
- Semantic Transformers.js embeddings are an explicit opt-in and require a rebuilt index.
- `@jcode.labs/ragmir-chat` adds optional cited synthesis with a local GGUF model.
- Qwen and Gemma are optional Chat profile choices, not Core or MCP requirements.
- `@jcode.labs/ragmir-tts` adds optional local audio or explicitly selected online speech.
- `@jcode.labs/ragmir-chat` adds optional local GGUF synthesis: `lite` uses Qwen, while default
`fast` and explicit `quality` use Gemma.
- `@jcode.labs/ragmir-tts` supports offline English, French, and Spanish; explicit online Edge mode
also supports Japanese, Thai, and Chinese.
- Chat profiles and TTS models are not Core or MCP requirements.
- Ragmir is not a hosted document store, cloud-sync service, agent-memory replacement, or universal
code-search replacement.

Expand Down
17 changes: 16 additions & 1 deletion packages/ragmir-tts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ npx rgr-tts render ./brief.md --lang en --offline --out .ragmir/audio/brief.wav
When Core is installed too, `npx rgr audio` delegates to the same package. TTS reads the text
provided by the caller; it does not retrieve evidence or write a summary.

## Choose a language

Use the same `--lang` code for preload and offline rendering. Ragmir selects the matching model;
French is the default only when `--lang` is omitted.

| Code | Language | Offline model selected automatically |
| --- | --- | --- |
| `en` | English | `Xenova/mms-tts-eng` |
| `fr` | French | `Xenova/mms-tts-fra` |
| `es` | Spanish | `Xenova/mms-tts-spa` |

Run `npx rgr-tts doctor --json` to inspect `offlineLanguages` and `edgeLanguages`. Japanese (`ja`),
Thai (`th`), and Chinese (`zh`) are available only through the explicitly selected online Edge
engine unless you supply a compatible Transformers.js model yourself.

## TypeScript API

```ts
Expand Down Expand Up @@ -76,7 +91,7 @@ npx rgr-tts render ./public-announcement.md \

| Path | Output | Languages | Network boundary |
| --- | --- | --- | --- |
| Transformers.js | WAV | English, Spanish, French | Model preload is explicit; rendering can then stay offline |
| Transformers.js | WAV | English, French, Spanish | Model preload is explicit; rendering can then stay offline |
| Edge | MP3 | English, Spanish, French, Japanese, Thai, Chinese | Narration text is sent to the external service |

There is no silent fallback from local rendering to Edge. Keep model state and generated audio
Expand Down
4 changes: 2 additions & 2 deletions packages/ragmir-tts/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ Usage:

Options:
--engine <engine> transformers, edge, or auto. Default is transformers.
--lang <language> en, es, fr, ja, th, or zh. Selects the language. Default fr.
--lang <language> Offline: en, es, fr. Edge also: ja, th, zh. Default fr.
--model <id> Transformers.js TTS model ID.
--model-path <path> Local model/cache path. Defaults to .ragmir/models/tts.
--offline Force the Transformers.js local/offline WAV path.
--allow-remote-models Explicitly allow remote model downloads.
--voice <voice> Edge voice. Defaults to fr-FR-DeniseNeural.
--voice <voice> Edge voice override. Default matches --lang.
--rate <rate> Edge rate. Defaults to +0%.
--speaker-embeddings <path> Optional model-specific speaker embedding path or URL.
--speed <number> Optional model-specific speech speed.
Expand Down
37 changes: 37 additions & 0 deletions scripts/smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ try {
"ragmir-tts",
"TTS help should not advertise the previous command alias",
)
assertIncludes(
ttsHelp.stdout,
"Offline: en, es, fr. Edge also: ja, th, zh.",
"TTS help should distinguish offline and Edge language support",
)
assertIncludes(
ttsHelp.stdout,
"Default matches --lang.",
"TTS help should explain automatic Edge voice selection",
)

const audioHelp = await runKb(["audio", "--help"], tempRoot)
assertIncludes(
audioHelp.stdout,
"Offline: en, es, fr.",
"Core audio help should list offline language support",
)
assertIncludes(
audioHelp.stdout,
"th, zh. Default: fr.",
"Core audio help should list the remaining Edge languages and default",
)

const deprecatedTtsCliPath = path.join(tempRoot, "ragmir-tts")
await symlink(ttsCliPath, deprecatedTtsCliPath)
Expand All @@ -102,6 +124,21 @@ try {
"ragmir-chat",
"chat help should not advertise the previous command alias",
)
assertIncludes(
chatHelp.stdout,
"lite (Qwen2.5 0.5B)",
"chat help should identify the lightweight Qwen profile",
)
assertIncludes(
chatHelp.stdout,
"fast (Gemma 4 E2B, default)",
"chat help should identify the default Gemma profile",
)
assertIncludes(
chatHelp.stdout,
"Lite always uses off.",
"chat help should explain the lite thinking boundary",
)

const deprecatedChatCliPath = path.join(tempRoot, "ragmir-chat")
await symlink(chatCliPath, deprecatedChatCliPath)
Expand Down