yq-nova-agent v0.4.1 發佈例行維護更新
yq-nova-agent v0.4.1 — a regular, periodic maintenance release
yq-nova-agent v0.4.1 發佈例行維護更新,為 Python 和 TypeScript SDK 加入命名空間管理,並修正重試設定錯誤時觸發 panic 的問題。伺服器現會在 recall 和 list 時強制使用 API key 綁定的命名空間,亦修正空標籤篩選及 LIKE 萬用字元處理。
What is yq-nova-agent?
If you've been around the AI-agent block for a while, you've probably hit the same wall I have: where do I put the agent's memory?
Vector DBs are heavy (Pinecone, Qdrant, Weaviate — all need a service). Mem0 is cloud-only with a non-trivial price tag. SQLite + FTS5 is fine, but then you still have to glue the embedding provider, the graph, the GC and the SDK yourself.
yq-nova-agent is a small Rust core that gives you all of that in one SQLite file:
-
remember / recall / forgetover HTTP or Rust SDK - Semantic search (pluggable embedding: OpenAI-compatible, Zhipu, Qwen/DashScope, Baichuan, Jina, mock)
- Hybrid ranking (semantic + FTS5 + graph), fused with RRF
- Entity-relation graph with BFS traversal
- Multi-tenant namespaces (
x-namespaceheader + per-namespace API keys) - Background GC (TTL + importance-based forgetting, graceful shutdown)
- One CLI:
yq-nova remember | recall | forget | stats | list | tags - Ecosystem: TypeScript SDK, Python client, LangChain and LlamaIndex integrations, an MCP server crate
If that sounds like the kind of "just plug it in and go" memory layer you wish you had — read on, because 0.4.1 is one of the most useful patch releases this project has shipped.
The headline changes in 0.4.1
1. Namespace parity for the Python and JavaScript SDKs (#77, #71)
Since v0.4.0, the server has had POST /v1/namespaces and GET /v1/namespaces for managing tenants. But until 0.4.1, the Python and TypeScript SDKs didn't expose those endpoints — so multi-tenant SaaS deployments were left with two options: drop down to raw fetch, or smuggle the request body through a server that doesn't validate names.
0.4.1 fixes that.
Python:
from yq_nova import YqNova
client = YqNova(base_url="http://localhost:7999")
ns = client.namespaces.create(name="acme", description="ACME tenant")
client.remember("user prefers dark mode", namespace="acme", importance=0.8)
recalls = client.recall("user preferences", namespace="acme", top_k=5)
Behavior to watch:
-
create_namespacerejects the reserveddefaultname and reuses the same character rules aswith_namespace, so the Python namespace name and the Rustwith_namespaceargument can never disagree. -
update_namespacesends only the fields you pass — the server leaves the rest untouched, so partial updates are safe.
JavaScript / TypeScript:
import { YqNova } from "@yq-nova/sdk";
const client = new YqNova({ baseUrl: "http://localhost:7999" });
const ns = await client.namespaces.create({ name: "acme" });
await client.remember({ content: "...", namespace: "acme", importance: 0.8 });
const hits = await client.recall({ query: "...", namespace: "acme", top_k: 5 });
Both SDKs are still 100 % wire-compatible with the existing /v1/namespaces HTTP routes, so a token minted server-side round-trips through either SDK without drama.
2. Embedding-retry no longer panics on bad config (#73)
I have a soft spot for crash-only bugs. This is one of those.
let cfg = RetryConfig {
max_attempts: 0, // "I really don't want any retries, just fail fast"
base_sleep: Duration::from_millis(50),
max_sleep: Duration::from_secs(2),
..Default::default()
};
// pre-0.4.1: panic. post-0.4.1: 1 attempt, returns Err(EmbeddingError::AttemptsExhausted).
Pre-0.4.1, RetryConfig { max_attempts: 0, .. } panicked on the first retry attempt — which is exactly what you don't want, because the surrounding caller was already written to handle Err, never the panic. Likewise, a base_sleep > max_sleep panicked because the sampling range was logically empty.
0.4.1 clamps:
-
max_attempts = 0→ forced to 1 (you'll still get one attempt before the error bubbles up). -
base_sleep > max_sleep→ both clamped into a valid sampling window. - The internal backoff uses
saturating_mul, so an extremebase_sleepvalue can't overflow.
The contract — "you get an Err, never a panic" — was already the documented behavior; 0.4.1 just makes it true.
3. Server now forces the authenticated namespace on recall and list (#70)
If you mint an API key tied to namespace acme and a curious developer writes:
curl -X POST http://127.0.0.1:7999/v1/memory/recall \
-H "Authorization: Bearer $ACME_TOKEN" \
-H "x-namespace: internal" \
-d '{"query":"foo"}'
…v0.4.0 would happily scope the query to internal, because the request body's / header's namespace was treated as authoritative.
0.4.1 makes the token the authoritative namespace for recall and list, full stop. If the request header disagrees, the server pins to the token's namespace. PR #70's name spells it out: force the authenticated namespace on recall and list filters.
This is the kind of PR that closes a CVE-shaped hole without making it a CVE — the existing single-tenant deployments are completely unaffected (they only have one namespace, and the token binds to it anyway).
4. Three SQL/LIKE correctness fixes (#53, #74, #72)
These read as small in the changelog; in production, each one would silently hurt someone.
#53 — empty tags_all filter now matches nothing:
// pre-0.4.1: tag-filter list → "match everything"
// post-0.4.1: tag-filter list → "match nothing", caller fixes the request
{ "tags_all": [], "query": "..." }
The fix is straightforward: an empty tags_all constraint is a hard constraint with zero categories to satisfy, so the SQL layer short-circuits to zero rows. This eliminates a silent over-recall bug where a UI bug accidentally landed an empty filter and the user got everything back.
#74 — LIKE wildcards are now escaped in entity-name prefix filters:
-- pre-0.4.1:
SELECT * FROM entity WHERE name LIKE '%100%off%';
-- Returns every entity whose name contains '100' followed by 'off'.
-- Completely misses an entity literally named '100% off'.
-- post-0.4.1:
SELECT * FROM entity WHERE name LIKE '%100\%off%' ESCAPE '\\';
-- Matches only the literal string '100% off'.
#72 — HTTP provider error bodies truncate on UTF-8 boundaries:
If a remote provider responded with a 4 KB error body containing a 4-byte emoji right at the truncation boundary, you used to get a half-character in your logs (or worse, in a downstream JSON encoder). Now the truncation walks to the nearest character boundary first.
5. CI governance gets tightened (#69, #60, #58, #75, #76)
A healthy project eventually needs to clamp down on its own automation. 0.4.1 does that:
- #69 scopes each workflow's concurrency group by workflow name, so re-running workflow A can't cancel reviewers for workflow B.
-
#60 teaches
@yq-pm-juniornot to review its own posts and not to duplicate a post inside the same review thread. - #58 wires a small YQ review-team (5 bots + PM gate) into PR review.
-
#76 adds
CODEOWNERSrequiring a human reviewer for everything under.github/. -
#75 explicitly steps
@yq-pm-junioraside on those same.github/PRs — those become human-only by design.
If you're a contributor: you'll see less bot noise in your PR threads. If you're a maintainer: you'll see less "did the bot really just approve itself?" drama.
What's intentionally not in 0.4.1
Open PRs that landed on the floor of the 0.4.0 → 0.4.1 window but didn't make the cut, because they were either still under review or landed slightly after the changelog was cut:
- #81 —
fix(js): correct MergeEntitiesOutput and type the remaining SDK responses - #80 —
feat(langchain, llamaindex): add knowledge-graph tools to both framework integrations - #79 —
feat(sdk): add graph query methods to EmbeddedNova - #78 —
feat(js): add namespace management methods to the TypeScript SDK - #57, #56, #55, #48, #47, #46, #45, #44, #42, #40, #37, #36, #35, #20 — various open fixes, refactors, perf work, and a feature about scoping MCP tool calls to a namespace
These will roll up into 0.5.0 (or whatever the next bump turns out to be).
How to upgrade
# Docker
docker pull yqteam/yq-nova:0.4.1
docker compose down && docker compose up -d
# crates.io
cargo install yq-nova-server --locked --version 0.4.1
# Source
git checkout v0.4.1
cargo build --release -p yq-nova-server --bin yq-nova
The yq-nova-data volume (SQLite at /data/yq-nova.db) is untouched. There is no data migration step.
Why a periodic maintenance release matters
v0.4.1 is the kind of release the project has been needing for a month, and it shows: the highlights aren't "look at this shiny new feature", they're "this panic is gone, this over-recall is gone, this silent privilege escalation is gone, and our SDKs now agree with the server".
That's the boring stuff that turns a project into a project you can build on.
If you want to follow along, the full release notes live here:
👉 https://github.com/YQteam-hq/yq-nova-agent/releases/tag/v0.4.1
And the changelog is the canonical source of truth:
👉 https://github.com/YQteam-hq/yq-nova-agent/blob/main/CHANGELOG.md
— The YQ team
來源:DEV Community · dev.to