Skip to content

Releases: ProjAnvil/LadyM

v0.8.1 — Hotfix: .ladyM Project-Local Default DB & ladym init

Choose a tag to compare

@HoweChen HoweChen released this 21 Oct 03:21

Highlights

A small but layout-defining hotfix on top of 0.8.0: the personal edition's default SQLite database moves from a flat ./ladym.db in your project root into a dedicated .ladyM/ directory, and a new ladym init command makes the setup explicit and reversible.

  • New default: .ladyM/ladym.db. One dot-directory now holds the db and all its sidecar files (-wal, -shm, .index.lock), keeping the project root clean — the same convention as .wrangler/state, .expo/, and friends. One .gitignore line (.ladyM/) covers everything, so forgotten -wal entries can no longer leak into commits.
  • ladym init. Creates .ladyM/ladym.db (idempotent schema bootstrap), migrates a legacy flat ./ladym.db — db plus -wal/-shm sidecars — into .ladyM/, and appends the two-line ignore block to .gitignore when one exists (or creates it in a git repo; prints a hint otherwise). Re-running is always safe.
  • Zero-breakage fallback. If a legacy flat ./ladym.db exists, it keeps being read — existing memories never vanish behind the new default. Migration happens only when you ask for it (ladym init). Explicit paths (--db, LADYM_DB, toml db_path) are untouched: init with an explicit path only bootstraps the schema.
  • Hook synergy. Hook events land in the per-project .ladyM/ladym.db (the hook process inherits the harness cwd), so silent capture from 0.8.0 now stays out of version control by default.

Compatibility

  • No schema changes; no config format changes. All explicit db-path configuration (--db, LADYM_DB, ladym.toml db_path) behaves exactly as before.
  • Existing flat ./ladym.db layouts keep working via the read fallback until migrated with ladym init (stop serve/worker before migrating).
  • Case note: the directory is spelled .ladyM (capital M), matching the existing ~/.ladyM home layout.

Installation

Prebuilt binaries for linux/darwin (amd64/arm64) — personal, personal-fulldict, and enterprise flavours — are attached below with SHA256SUMS. Or build from source:

go build -o bin/ladym ./cmd/ladym

Then, in your project:

ladym init

v0.8.0 — Silent Hook Capture & Plugin Market

Choose a tag to compare

@HoweChen HoweChen released this 21 Oct 03:21

Highlights

This release gives LadyM a silent capture path: Claude Code and Codex now feed memory automatically through their lifecycle hooks, without the agent spending a single token or knowing it is happening. Until now every memory write required the agent to choose to call a tool (remember / record_event); starting with 0.8.0, the harness itself records sessions — closing the biggest gap identified in our comparison with ai-memory: a memory system is only as good as its feed.

  • ladym hook — the hook carrier. One subcommand serves both harnesses: it reads the event JSON from stdin, maps it onto a structured L1 episode through the record_event pipeline (controlled-vocabulary action classes, resource extraction from tool_input, deterministic operates_on edges), writes it, and exits. Observer semantics throughout: stdout stays empty, unknown events and unknown fields are tolerated (schema drift never breaks the binary), and the exit code is never 2 — it can never block or feed decisions back into the harness.
  • ladym hook install / uninstall. One command merges the observer entries into ~/.claude/settings.json and ~/.codex/hooks.json. User entries and non-hook keys survive verbatim, installs are idempotent (prefix-matched), a one-time .ladym-bak backup is taken before the first write, and corrupt config files are refused rather than overwritten. Uninstall removes exactly our entries and nothing else.
  • Plugin market. plugins/ladym (Claude Code plugin: hooks + MCP server) and plugins/ladym-codex (Codex plugin) ship the same wiring as installable plugins, catalogued under the ladym-plugins marketplace in this repository. Plugins carry no binaries — every command points at the ladym on your PATH, so the binary remains the single source of truth.
  • Zero-config db placement. Hook events default to a per-project ladym.db (the hook process inherits the harness cwd), matching the workspace inference (event cwd's directory name). Set LADYM_DB for a unified library — the workspace then becomes the isolation key. Changing the db never requires reinstalling hooks.

What's Changed

Silent Hook Capture

  • hook package — tolerant parsing of the shared Claude Code / Codex hook event shape (unknown fields ignored), workspace inference (WorkspaceFromCwd), tool-name → action-class mapping onto the canonical 31-word vocabulary (MapToolActionClass; MCP tools and unknown names stay raw for the worker's LLM backfill), and ToOperation for the V1 event set: SessionStart (create), SessionEnd, UserPromptSubmit (prompt observation, bounded at 2000 runes), PostToolUse / PostToolUseFailure (outcome success / fail, file-path resources from tool_input, tool response bounded at 500 runes), and Stop.
  • ladym hook CLI — pipes stdin through the mapping into Engine.Scope(workspace).RecordOperation. Integration tests prove the full chain: event JSON → episode in the db → operates_on edge → retrievable via ladym recall-ops --resource …. The --harness flag (written by the installer) lands in the Agent field, since the two harnesses' event JSON is identical.

Installer

  • Merge, never overwrite. The installer reads the whole config document and writes it back — keys like model in settings.json survive. Entries are recognised as ours by the ladym hook command prefix, so --harness differences and future flag changes stay idempotent.
  • Safety rails. One-time .ladym-bak backup (never rolled over), parent directories auto-created, corrupt JSON refused with a clear error, uninstall is a no-op when the file is absent, and a file that held nothing but our hooks is removed entirely.
  • Post-install guidance. Claude Code: restart your session. Codex: run /hooks to complete the trust review (Codex binds trust to the hook definition hash). A warning is printed when ladym is not on PATH.

Plugins & Marketplace

  • plugins/ladym/ — .claude-plugin/plugin.json manifest, six-event hooks/hooks.json (including the Claude Code-only PostToolUseFailure), and .mcp.json registering ladym serve as the plugin's MCP server.
  • plugins/ladym-codex/ — .codex-plugin/plugin.json manifest (explicit hooks / mcpServers pointers), five-event hooks/hooks.json with 10-second timeouts.
  • plugins/.claude-plugin/marketplace.json — the ladym-plugins catalog. Add it from the /plugin browser or validate locally with claude plugin validate plugins/.
  • Five static-validation tests pin the event sets, command strings, manifests, and MCP configs, so the plugin files cannot drift from the installer.

Documentation

  • docs/hooks.md — the personal-edition install guide: installation for both harnesses, db placement (per-project default vs LADYM_DB unified library), workspace inference, the event → memory mapping table, verification via recall-ops, plugin installation, troubleshooting, and the honest known-tradeoffs section.
  • docs/research/ — the two research notes behind this release: the ai-memory-vs-LadyM comparison and the hook-carrier selection (Go binary over Python/sh, with the verified hook mechanics of both harnesses).
  • README (en/zh) and ARCHITECTURE.md updated with the new front-end and project layout.

Compatibility

  • No schema changes, no config breaking changes. Existing MCP tools, CLI commands, and SDK surfaces are untouched; record/remember keep their exact 0.7 behaviour (locked by regression tests).
  • The new capture path is opt-in: nothing is recorded until you run ladym hook install or install a plugin.
  • Enterprise edition is unaffected — capture is a personal-edition concern (the enterprise edition is the memory database; clients feed it).

Known Limitations

  • No capture-side sanitization yet. Prompts and tool outputs land as captured; a db is a local file, so the exposure surface equals your disk. Path exclusion and secret redaction are planned for the next release — tracked in docs/hooks.md.
  • V1 event set only. PreToolUse, PreCompact, and notification events are not captured (deliberate: no double-writes, bounded L1 growth). Unknown events are tolerated silently and can be added later without breaking installed hooks.
  • Codex plugin manifest follows the documented .codex-plugin/plugin.json shape as of this release; the plugin systems of both harnesses are still evolving quickly, so the plugin files are intentionally thin (one manifest + one hooks file each).

Installation

Prebuilt binaries for linux/darwin (amd64/arm64) — personal, personal-fulldict, and enterprise flavours — are attached below with SHA256SUMS. Or build from source:

go build -o bin/ladym ./cmd/ladym

Then:

ladym hook install --claude   # or --codex, or --all

Full guide: docs/hooks.md.

v0.7.0 — Operation-History Retrieval & Self-Maintaining L4 Graph

Choose a tag to compare

@HoweChen HoweChen released this 19 Oct 05:54

Highlights

This release gives agents a first-class operation-history memory: recording what was done to which resource, and retrieving it back by resource and action instead of free-text similarity. It also turns the L4 associative graph from a passive index into a self-maintaining structure — deterministic projections at write time plus a budgeted maintenance step in the System 2 worker.

  • Operation-history retrieval (recall_ops). A resource × action pivot query answering "how did I last operate on X?". Vector search is confined to resource resolution; an L4 operates_on graph join does the exact filtering; ACT-R activation ranks the results. The read path is LLM-free, and unresolvable resources degrade gracefully to plain recall.
  • Structured record_event slots. Optional resources and action_class arguments resolve against a resource registry (L2 type=resource, lazily-grown aliases) and link operates_on edges deterministically at write time.
  • Self-maintaining L4 graph. Write-time deterministic projections (operates_on, produced_from, calls/imports from code_refs — all idempotent via ProjectedEdgeID upserts) plus a new MaintainL4 System 2 step: dangling-edge GC every cycle, budgeted slot backfill, gated semantic linking, and resource alias-merge.
  • Re-index hygiene. Re-indexing a changed file purges its stale symbol memories, code_symbols/code_refs rows and in-process vector entries first — removed symbols no longer linger.
  • Documentation matched to code. Architecture claims that ran ahead of the implementation are now accurate, including an honest scoping note: code-ref projection covers same-file call/import relations today; cross-file extraction is future work.

What's Changed

Operation-History Retrieval

  • recall_ops — new engine query (Scope.RecallOperations / Engine.RecallOperations) with a three-stage pipeline: Stage 1 resolves the resource (exact alias match → vector nearest-neighbour over type=resource memories at a read-side threshold of 0.75, below the write-side 0.85); Stage 2 joins L4 operates_on edges and filters by normalised action, time window and outcome; Stage 3 ranks with ACT-R activation. Exposed as the 10th MCP tool (recall_ops), a CLI command (ladym recall-ops --resource … --action … --outcome …), and Go SDK one-shot helpers (ladym.RecordOperation / ladym.RecallOperations).
  • Fallback semantics. Unknown resources never error the caller: the query degrades to a plain recall(resource + " " + action) and the response is flagged fallback=true. Cold-start (no resource entities at all) behaves identically.

Structured Event Recording

  • record_event accepts optional resources (names or aliases) and action_class. Each resource resolves through the registry — reusing an existing entity, growing its alias list via a vector match, or creating a new one — and the episode gets a deterministic operates_on edge per resource (ProjectedEdgeID, idempotent upsert). Omitting the new fields reproduces the legacy behaviour byte-for-byte (locked by regression tests).
  • Action vocabulary. action_class normalises against a controlled vocabulary — 31 canonical words across knowledge/code/ops/communication domains plus built-in aliases (run→execute, rm→delete, grep→search, …). Exact match only: unknown actions are preserved as action_raw for the worker to backfill, never guessed. Extend via the [resource] config section (actions, action_aliases).

L4 Graph Maintenance

  • Write-time deterministic projections. operates_on (episode/fact → resource) from record_event; produced_from (fact → source episode) plus operates_on inheritance from consolidation ADD/UPDATE; calls/imports (symbol → symbol) projected from code_refs at index time. All machine edges use ProjectedEdgeID (proj: + truncated SHA-256 of src|relation|dst) so re-running a projection upserts instead of duplicating.
  • MaintainL4 worker step (System 2 cycle, after Decay):
    • Dangling-edge GC runs every cycle as correctness hygiene (defence-in-depth for pre-FK databases and external writers; current schema already cascades).
    • Slot backfill — heuristic exact-match stamping of action_class plus optional LLM extraction of resources/actions for episodes recorded before this release (l4_backfill op).
    • Semantic linking — gated (config flag, LLM availability, minimum recent episodes) same-(layer, action_class)-bucket candidate pairs, similarity-prefiltered and budgeted per cycle (l4_link op).
    • Alias merge — high-similarity resource pairs confirmed by the LLM (l4_alias_merge op) merge into one entity: aliases unioned, incoming operates_on edges re-pointed, superseded entity retired via the standard supersedes chain.
  • Three new agent ops registered in NAMED_OPS (l4_backfill, l4_link, l4_alias_merge) — each independently configurable via [agents], each fully optional (no LLM configured ⇒ the steps skip cleanly).

Correctness & Storage

  • NeighborCounts endpoint filtering — neighbour centrality no longer counts edges whose endpoint memory is gone (EXISTS join on both endpoints, both backends).
  • SweepDanglingEdges() — new Store method deleting valid edges with missing endpoints, wired into every System 2 cycle.
  • DeleteCodeArtifactsByFile() — new Store method for re-index hygiene: removes a file's code_refs (via subquery, ordered before code_symbols), code_symbols rows, and symbol memories, and keeps the SQLite in-process vector index in sync.
  • Consolidation provenance — ADD/UPDATE now project produced_from and inherit operates_on targets onto the new fact. A dead Python-parity branch in the offline classifier (superseded_by == candidateHash, comparing a UUID against a content hash — never true) was removed; replay protection is carried by the content-hash equality check, locked by a regression test.

Documentation

  • ARCHITECTURE.md: L1 row documents the resource × action slots; L4 row replaced the aspirational "auto-link on write" with the actual write-time projections + worker maintenance; the storage layout lists all edge relations; new §3b documents the recall_ops pipeline; the MCP front-end lists ten tools.
  • README (en + zh-CN): new "What's New in 0.7.0" sections, recall_ops usage for MCP and CLI, [resource]/[maintain] configuration reference.

Testing & Quality

  • Coverage (new code on this branch): operations 94.6%, engine 96.5%, code 95.4%; eleven functions initially under 80% were driven to 100% with edge-case suites (+1,240 lines of tests, zero production-code changes); storage reaches 97%+ with the Postgres suite live (make test-pg).
  • New suites are scenario-named across the stack: neighbor_counts_join_test.go, sweep_dangling_test.go, record_operation_test.go, consolidate_provenance_test.go, delete_code_artifacts_test.go, indexer_projection_test.go, recall_operations_test.go, maintain_test.go, maintain_cycle_test.go, and per-file coverage suites.
  • go vet clean on both the default and enterprise build configurations; fixture-dependent test files carry //go:build !enterprise tags.

Full Changelog: v0.6.0...v0.7.0

v0.6.0 — Concurrency, High Availability, Observability & Multi-Language SDKs

Choose a tag to compare

@HoweChen HoweChen released this 13 Sep 04:12

Highlights

This release removes the last single-threaded bottleneck in the HTTP data plane, makes multi-replica System 2 worker deployments safe, adds first-class Prometheus observability, and ships official Python and TypeScript SDKs alongside the Go client.

  • Concurrent HTTP data plane. The global request mutex that serialized every engine call is gone. Requests across workspaces now run fully in parallel; writes serialize only at the storage backend (SQLite WAL single-writer; Postgres fully parallel).
  • Multi-replica System 2 workers. Cycle-level leader election (Postgres advisory lock / SQLite flock) lets you run standby workers for failover without duplicate consolidation cycles.
  • Prometheus metrics. A native /metrics endpoint backed by prometheus/client_golang, plus a --metrics-addr flag that exposes metrics from the worker role.
  • Python & TypeScript SDKs. Zero-runtime-dependency clients for the HTTP data plane, feature-parity with the Go client.
  • Test coverage raised to 97.9% (Postgres mode), with scenario-named suites across every package.

What's Changed

Performance & Concurrency

  • engine.Scope — a zero-I/O, workspace-bound engine view that shares Store/Provider/LLM-agent caches. Per-request state mutation (withWorkspace) is eliminated; the two write endpoints route through scopes. Non-default workspaces get isolated L0 buffers via sync.Map.
  • Handler.mu removed — all 11 lock sites deleted from the API layer. Concurrency is now guarded by fine-grained component locks: the engine's agent-cache mutex, per-workspace working-memory mutexes, and a new RWMutex on the previously unlocked InMemoryVectorIndex.
  • langgraph retain node no longer constructs a short-lived engine (and store connection) per request — it uses Scope instead.
  • A 32-goroutine × 4-workspace mixed-workload test verifies isolation and runs clean under -race.

High Availability

  • Store.TryAcquireWorkerLock() — new cross-process mutual exclusion for System 2 cycles, mirroring the existing index-lock design: session-scoped Postgres advisory lock (key 727276) and SQLite flock(<db>.worker.lock). Both release automatically on process/session death.
  • Worker loop and engine.StartSystem2 acquire the lock per cycle: a held lock skips the cycle (standby, not a failure); a crashed replica's work is picked up by any survivor on the next cycle. deploy.replicas: 2+ is now safe for the worker role.

Observability

  • GET /metrics (auth-exempt, outside /api/) serves a Prometheus text exposition via client_golang: ladym_http_requests_total{endpoint,status}, ladym_http_request_duration_seconds histogram, ladym_http_requests_in_flight, ladym_system2_cycles_total{result}, ladym_go_goroutines. Endpoint labels use route patterns, so id-addressed paths cannot explode cardinality; pre-routing rejections (401s) count under endpoint="unknown".
  • ladym worker --metrics-addr :9090 serves /metrics + /healthz from the worker role.
  • The existing /api/metrics JSON shape is unchanged and now reads from the same registry.

SDKs

  • sdk/python (ladym-client, uv, stdlib-only): remember/recall/record_event/consolidate/stats/link/forget + memories CRUD, LadymError with HTTP status, injectable transport, 100% statement coverage.
  • sdk/typescript (ladym-client, zero runtime deps, Node ≥ 20 / browsers): camelCase API surface with injectable fetch, AbortSignal timeouts, 100% line coverage.
  • Both ship unit suites plus a gated integration leg (LADYM_SDK_IT=1) that builds ladym, serves a throwaway SQLite instance, and runs the full round-trip.

Testing & Quality

  • Coverage: 97.9% in Postgres mode (make test-pg), up from 95.2%; engine 100%, api 99.6%, cli 98.9%, storage 98.2% (PG), cmd/ladymconsole 0→100% on both editions. New suites are scenario-named (e.g. worker_lock_failover_test.go, cjk_dict_global_state_test.go).
  • Fixed a vector-index delete test whose branch coverage depended on map iteration order, and floored the read-path p95 baseline so the scaling budget is stable on loaded CI runners.

Breaking Changes

None. All public APIs (engine, api, MCP tools, CLI) are backward-compatible; Engine.Remember/RecordEvent signatures are unchanged.

Behavioral notes

  • /api/metrics endpoint keys are now route patterns (/api/memories/{id}) instead of raw paths, preventing unbounded growth of the JSON map.
  • The /metrics response Content-Type carries escaping=underscores (client_golang 1.24 behavior).

Upgrade Guide

  • Docker / Compose: pulling the v0.6.0 image is sufficient; no configuration migration. To run standby workers, raise the worker service's replica count — cycles are mutually exclusive automatically.
  • Prometheus: add a scrape job per api replica and per worker (--metrics-addr); see docs/deployment.md §4.
  • Go library users: no code changes required; engine.Scope is additive.
  • Postgres-backed coverage runs: make test-pg with LADYM_TEST_PG_DSN pointing at a pgvector instance.

Dependencies

  • Added: github.com/prometheus/client_golang v1.24.1 (plus standard transitive deps).
  • Runtime dependencies of the SDKs: none.

Full Changelog: v0.5.4...v0.6.0

v0.5.4 — Dependency Hotfix: langchain-golang v0.7.0

Choose a tag to compare

@HoweChen HoweChen released this 12 Sep 13:37

What's Changed

Dependency hotfix release — no behavior changes in LadyM itself.

langchain-golang v0.6.3 → v0.7.0

Audited every behavior/breaking change across v0.6.4, v0.6.5, and v0.7.0 against LadyM's usage surface (core/language, core/messages, core/modelconfig, core/schema, core/embeddings, core/tools, partners/openai|anthropic|ollama, langgraph/graph node funcs, langgraph/runtime):

  • DrawASCII/DrawMermaid output format change — not used
  • CreateAgent/ToolStrategy.HandleErrors/recursion-limit changes — not used
  • DefaultRetryOn retries every error — LadyM compiles no graphs with retry policies; unaffected
  • Subgraph checkpoint namespaces per-task — no checkpoint savers used; unaffected
  • nil-Decide HITL interrupt mode, minted AI-message ids — agents middleware not used
  • Anthropic terminal usage-only stream chunk — no stream consumers; unaffected
  • runnables.Func child config (RunID) — not used
  • Benefit: openai Chat Completions now honors InvokeStructured/WithStructuredOutput response_format — fixes a silent gap on LadyM's structured-output path

Other direct dependency bumps

  • github.com/jackc/pgx/v5 v5.10.0 → v5.11.0
  • github.com/odvcencio/gotreesitter v0.49.0 → v0.52.0
  • golang.org/x/crypto v0.55.0 → v0.57.0
  • golang.org/x/sys v0.47.0 → v0.48.0
  • modernc.org/sqlite v1.56.0 → v1.58.0

Full test suite green (go build ./..., go test ./...).

Full Changelog: v0.5.3...v0.5.4

v0.5.3

Choose a tag to compare

@HoweChen HoweChen released this 01 Sep 07:43

Hotfix Release

New: Hermes Agent memory provider plugin (integrations/hermes/)

Use LadyM as the persistent memory backend for Hermes Agent:

# 1. install the plugin (subdirectory install — no manual file copying)
hermes plugins install ProjAnvil/ladyM/integrations/hermes

# 2. activate the provider, then bootstrap the ladym binary
#    (the hermes ladym CLI only exists once ladym is the active provider;
#     add --fulldict for the embedded CJK dictionary)
hermes memory setup ladym
hermes ladym install

# 3. verify
hermes memory status   # Provider: ladym, available ✓
hermes ladym status    # binary path, effective config, store stats
  • Zero-dependency Python adapter that drives the ladym binary over MCP stdio — the Go engine remains the single source of truth.
  • Recalls through LadyM's two-tier recall before each turn, records every turn as an L1 episodic event, and consolidates (L1 → L2) at session end and before context compression.
  • Per-profile SQLite store at $HERMES_HOME/ladym/ladym.db; offline hashing embeddings by default — no API key, no external service.
  • hermes ladym install downloads the matching release binary (plain or fulldict variant) with SHA256SUMS verification.
  • Also installable via pip entry point: pip install 'git+https://github.com/ProjAnvil/ladyM.git#subdirectory=integrations/hermes'.

Docs: new "Hermes Agent (memory provider)" section in both READMEs; full plugin docs in integrations/hermes/README.md.

Full Changelog: v0.5.2...v0.5.3

v0.5.2 — bounded consolidation: backlog-only scans, ADD rewrites, since bound

Choose a tag to compare

@HoweChen HoweChen released this 27 Aug 03:49

What's Changed

Fixes

  • Consolidation cost is now bounded by the backlog, not the store — every episode
    that completes a consolidation pass (whatever the verdict: ADD / UPDATE / DELETE /
    NOOP) is stamped with a consolidated_at metadata mark and never re-selected, so
    each System 2 cycle pays for new episodes instead of re-embedding and re-classifying
    the full history. Unlike a caller-side since timestamp, the stamp survives aborted
    passes. Reports expose skipped_consolidated across the engine, HTTP API, MCP
    server, and Go client. (d42f856)
  • ADD verdicts store the LLM's rewritten fact — promoted facts no longer carry the
    raw event's recording template shell (agent=… | action=… | observation=…). The
    classifier's new_text is now honored on ADD as well as UPDATE, falling back to the
    raw event text when omitted — the offline (no-LLM) classifier always omits it, so
    heuristic mode is unchanged. The consolidation prompt and JSON schema now describe
    new_text as the distilled fact for both verdicts. (c841747)

Features

  • since bound on consolidate — /api/consolidate, the MCP consolidate tool,
    and Consolidate(ctx, workspace, since) on the Go client accept an optional
    unix-seconds since (0 = the whole pending backlog), giving remote deployments an
    explicit cost-control handle. (da06014)

Documentation

  • README (en + zh-CN) What's New refreshed for the 0.5 line; version bumped to 0.5.2.
    (393d8b1)

Upgrade notes

  • Downstream integrations that worked around the full re-scan (e.g. assembling their
    own incremental cycles with caller-side since threading) can drop that logic:
    Consolidate(ws, 0) now means "the pending backlog", not "the whole store".
  • The Go SDK's Consolidate signature gained a since float64 parameter — pass 0
    for the previous behavior.

Full changelog: v0.5.1...v0.5.2

v0.5.1 — CJK support: dictionary-backed Chinese/Japanese/Korean tokenization

Choose a tag to compare

@github-actions github-actions released this 26 Aug 11:20

Highlights

LadyM now understands Chinese, Japanese, and Korean. CJK text tokenizes out of the box (fully offline), and optional dictionary-backed word segmentation upgrades recall quality — downloaded on demand from the console, baked into Docker images, or compiled into the binary. LadyM never downloads anything on its own.

This release also introduces automated multi-platform releases: every v* tag now publishes prebuilt tarballs for Linux/macOS × amd64/arm64 with a SHA256SUMS file.

What's New

🀄 CJK tokenization (Chinese / Japanese / Korean)

The native hashing tokenizer previously matched only [A-Za-z0-9_] — CJK queries tokenized to an empty set and always scored similarity 0. Now:

  • Han runs segment by dictionary word via go-ego/gse (the jieba algorithm, pure Go); kana and hangul tokenize per character with adjacent bigrams.
  • Zero-dictionary fallback: without any dictionary, CJK still tokenizes per character — recall keeps working, nothing to configure, nothing to download.
  • The default binary stays lean (50MB): gse's embedded dictionaries only link in when explicitly requested (see below).

📦 The dictionary system

Four downloadable variants, sha256-pinned to gse v1.0.2:

Variant Content Download
zh (default) Chinese, simplified + traditional 8.2 MB
zh_s Chinese, simplified only 4.9 MB
zh_t Chinese, traditional only 3.4 MB
jp Japanese, kanji + kana 22.6 MB

Design guarantees:

  • User-triggered only — downloads happen via the console or the admin-gated POST /api/cjk_dict/download; the tokenization path never touches the network (enforced by a contract test).
  • Integrity first — every file is sha256-verified before anything is written; failed or tampered downloads leave the existing dictionary untouched.
  • Hot reload — downloads and variant switches take effect immediately, no restarts; switching variants cleans up the old files.
  • Internal mirrors — {"mirror_base": "..."} points downloads at an air-gapped internal mirror.

🖥️ Console: Settings → Memory

A new admin Settings page manages the dictionary: variant picker, download/switch/remove, live status (source, version, size, directory). Works in both the personal embedded console and the enterprise ladymconsole service.

🐳 Dict-baked images (Docker)

One command builds and runs every role with the dictionary baked into the image — no shared volume, no downloads, no internet at runtime:

docker compose -f docker-compose.dev.yml -f docker-compose.dev.dict.yml up -d --build
docker compose -f docker-compose.enterprise.yml -f docker-compose.enterprise.dict.yml up -d --build

The dictionary ships as a thin data layer (/opt/ladym/dict) on top of the regular image: plain docker build . stays dictionary-less, the layer is architecture-independent, air-gapped builds work by placing the pinned files under dict/, and Dockerfile.dict --build-arg BASE=<image> stacks the layer onto any already-released image.

For fleets without baked images, the shared-volume pattern remains: point every instance at one directory (LADYM_DICT_DIR), download once on any instance, and all instances follow within ~30 seconds.

⚙️ Dictionary directory configuration

The dictionary directory (default ~/.ladyM/dict) is now configurable on all three daemon commands, with the usual priority chain:

ladym serve --http :8080 --dict-dir /data/cjk-dict   # flag
LADYM_DICT_DIR=/data/cjk-dict ladym serve --http :8080  # env
# ladym.toml: dict_dir = "/data/cjk-dict"             # toml

📚 Downstream Go consumers

Three ways to get the embedded dictionary — the module version never changes:

  1. Runtime download — trigger via console/API, or call storage.DownloadCJKDict().
  2. Side-effect import — import _ "github.com/ProjAnvil/LadyM/storage/fulldict" (no build-script changes).
  3. Build tag — -tags fulldict (what the ladym-personal-fulldict-* assets below use).

There is deliberately no v0.5.1-fulldict module version: semver reads that suffix as a pre-release of v0.5.1 and it would confuse go get.

Upgrading

No breaking changes; no migration needed. Optional:

  • For better CJK recall quality, download a dictionary: console Settings → Memory, or curl -X POST -H 'Content-Type: application/json' -d '{"dict":"zh"}' http://localhost:8080/api/cjk_dict/download (admin auth applies).
  • Docker users can switch to dict-baked images with the compose overrides above.

Release Assets

Built automatically by .github/workflows/release.yml (test gates → 4-platform matrix → upload):

Asset What it is
ladym-personal-<ver>-<os>-<arch>.tar.gz Personal edition (dictionary downloadable at runtime)
ladym-personal-fulldict-<ver>-<os>-<arch>.tar.gz Personal edition with the zh dictionary compiled in (~+31MB)
ladym-enterprise-<ver>-<os>-<arch>.tar.gz Enterprise edition (ladym + ladymconsole, Postgres-only)
SHA256SUMS Checksums for every tarball

Platforms: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64. Static binaries (CGO_ENABLED=0).

Full changelog: v0.5.0...v0.5.1

v0.5.0 — langchain-golang v0.6.2: reasoning effort & native JSON mode

Choose a tag to compare

@HoweChen HoweChen released this 25 Aug 17:06

Highlight: langchain-golang v0.5.0 → v0.6.2

LadyM's LLM/LangGraph foundation jumps three releases (v0.6.0 → v0.6.1 → v0.6.2) and puts the new capabilities to work:

  • v0.6.0 — Python SDK parity completion: OpenAI tool_choice, native JSON mode / raw response_format passthrough, with_structured_output improvements, model token counting APIs, checkpoint saver management API, prebuilt react agent helpers.
  • v0.6.1 — token counting parity: real tiktoken BPE for OpenAI (cl100k_base / o200k_base, official vision-token formula), Anthropic /messages/count_tokens backing, usage-metadata-scaled approximate counting.
  • v0.6.2 — tiktoken-go/tokenizer v0.8.1. Requires Go ≥ 1.26 (LadyM already targets go 1.26.4).

What's new in LadyM

  • reasoning_effort config knob, plumbed end to end — steer OpenAI o-series / gpt-5 reasoning models (low | medium | high):
    [llm]
    reasoning_effort = "high"
    Also via LADYM_LLM_REASONING_EFFORT and per-operation [agents.<op>] overrides. Validated at provider construction; anthropic / ollama / http accept and ignore it.
  • OpenAI-native JSON mode for structured output — structured_method = "json_mode" now binds the provider-native response_format={"type":"json_object"} on OpenAI partner models (upstream enforcement) while retaining the schema system prompt, replacing the former prompt-only path. Non-OpenAI models are unchanged.

Verification

  • go build / go vet green on both editions (personal + -tags enterprise)
  • make test-all green: personal + live Postgres, enterprise, comment lint
  • Statement coverage 95.8% (personal + PG); providers 99.5%, config 99.1%
  • TDD throughout (RED witnessed per task); two per-task reviews + final whole-branch review: merge-ready

Full Changelog: v0.4.0...v0.5.0

LadyM v0.4.0

Choose a tag to compare

@HoweChen HoweChen released this 20 Aug 03:42

LadyM v0.4.0

Highlights

  • Dual edition: personal (SQLite/Postgres, embedded console) and enterprise (-tags enterprise, Postgres-only microservices)
  • REST API service: every MCP tool is now also available over HTTP, plus /healthz and /api/metrics
  • Lightweight auth: database-level username/password (bcrypt, HTTP Basic), optional no-password users, [auth] enabled toggle; ladym user add/list/delete/passwd
  • Vue admin console: Login / Memories / Users / Stats with full CRUD API; embedded in personal build, split into standalone ladymconsole config-center microservice for enterprise
  • client/golang: Go SDK for embedding LadyM into other Go programs
  • Enterprise compose stack: three-tier docker-compose.enterprise.yml (nginx gateway -> ladym api x2 + console -> Postgres)

Artifacts

  • ladym-personal-v0.4.0-darwin-arm64.tar.gz — personal edition binary
  • ladym-enterprise-v0.4.0-darwin-arm64.tar.gz — enterprise binaries (ladym + ladymconsole) with compose stack