Skip to content

feat(runed): runed daemon gRPC client implementation - #101

Merged
couragehong merged 6 commits into
feat/go-migrationfrom
couragehong/feat/runed-grpc
May 7, 2026
Merged

couragehong merged 6 commits into
feat/go-migrationfrom
couragehong/feat/runed-grpc

Conversation

@couragehong

@couragehong couragehong commented May 5, 2026 •

Copy link
Copy Markdown
Contributor

요약

v0.4 go-migration의 외부 runed 데몬(임베딩 전담 sibling 프로세스) gRPC 클라이언트 구현입니다.

  • 4 RPC (Embed / EmbedBatch / Info / Health)
  • D7 retry [0, 500ms, 2s] + sync.Once Info 캐시 + batch split (len > MaxBatchSize 일 때) + Status enum→string 매핑.
  • mock-server unit test

runed 모듈이 아직 public registry 에 publish 안 돼서 (go get이 안됨) 로컬 replace directive 사용

  • ../runed 위치에 sibling repo clone 하고 한 번 buf generate 돌려서 gen/runed/v1/*.pb.go 만들어줘야 빌드됨 (runed 의 gen/ 은 gitignore).

runed/client Plan A wrapper 안 쓰고 raw runedv1.RunedServiceClient stub 직접 사용 — wrapper 가 Health() 안 노출해서 (rune_diagnostics MCP tool 에 필요).

base 는 wip/go-impl-runtime. #93/95/96 머지 후 retarget 예정입니다.


Scope

Task 3 of the v0.4 Go migration runtime impl: implement the runed embedding daemon gRPC client.

6 commits, 3 files of source + 1 audit doc (~659 LoC):

  • build(runed): add runed dep + initial gRPC dial wiring — github.com/CryptoLabInc/runed/gen/runed/v1 import; UDS dial
  • feat(runed): implement Info RPC + sync.Once cache — slog.Info "embedder info loaded" breadcrumb (D30)
  • feat(runed): implement EmbedSingle + EmbedBatch (batch split + retry) — D7 [0, 500ms, 2s]; resp count guard
  • feat(runed): implement Health RPC + enum→string mapping — STATUS_* → 5-string mapping
  • test(runed): mock-server unit tests + NewBufconnClient injection — 12 fn / 17 subtest (retry, split, cache, enum)
  • docs(v04): add spec parity audit for runed-grpc — 13 spec parity items, 4 follow-up gaps

Stacked-PR base

Base = wip/go-impl-runtime, an integration branch that local-merges
the still-under-review #93/#95/#96. With this base, the PR diff is exactly
the runed-grpc work — no noise from the prerequisite ports.

When #93/#95/#96 merge upstream, the cleanup is the same as the vault PR:
rebase wip + retarget this PR's base.

Spec

  • docs/v04/spec/components/embedder.md (post docs(v04): align spec with shipped runed/vault/envector SDKs #97)
  • 5 RPCs: Embed / EmbedBatch / Info / Health / Shutdown (Shutdown not used)
  • D7 retry policy + D30 model identity logging breadcrumb
  • Unix socket only for Plan A (Windows deferred to runed Plan B)
  • Full audit at docs/v04/notes/audit-runed-grpc.md

Local dep replace

go.mod adds:

require github.com/CryptoLabInc/runed v0.0.0
replace github.com/CryptoLabInc/runed => ../runed

The runed module isn't yet published. Local replace points to the sibling
checkout. Developers must (1) clone runed at ../runed, and (2) run
cd ../runed && buf generate (or make proto) once to materialize
gen/runed/v1/*.pb.go (gitignored upstream).

Notes

  • Hybrid choice: We use the raw runedv1.RunedServiceClient stub rather than the runed/client Plan A wrapper. The wrapper does its own Connect-time Health verify but does not expose a public Health() — which we need for the rune_diagnostics MCP tool.
  • toolchain: runed requires go 1.26.2. This PR keeps go 1.25.9 in our go.mod (replace bypasses the toolchain check); a separate bump should land before publishing.

@couragehong couragehong self-assigned this May 5, 2026
@couragehong
couragehong marked this pull request as draft May 5, 2026 23:12
@couragehong
couragehong force-pushed the wip/go-impl-runtime branch from 12a7b11 to 6d2a85a Compare May 6, 2026 22:33
@couragehong
couragehong force-pushed the couragehong/feat/runed-grpc branch from 0bb61d0 to ecce24e Compare May 6, 2026 22:35
@couragehong
couragehong changed the base branch from wip/go-impl-runtime to feat/go-migration May 6, 2026 22:39
@couragehong
couragehong marked this pull request as ready for review May 6, 2026 22:40
Adds github.com/CryptoLabInc/runed via local replace (../runed) — the
runed module is a sibling project not yet published to a public registry.
buf-generated stubs at runed/gen/runed/v1 are gitignored in runed and
materialized via 'buf generate' at build setup time.

In the embedder adapter:
- import runedv1 and grpc{,/credentials/insecure}
- New() dials 'unix://'+sockPath with insecure creds (UDS = same machine)
- client struct gains conn / pb / info (sync.Once cache) fields
- info_cache gains svc runedv1.RunedServiceClient field

EmbedSingle / EmbedBatch / Info / Health are still stubs; subsequent
commits implement them. Close() now drains conn.
infoCache.Get() calls runedv1.RunedServiceClient.Info on first invocation
and caches both snapshot and error for the lifetime of the adapter.
Subsequent Get() calls return the cached values without RPC.

slog breadcrumb 'embedder info loaded' (daemon_version / model_identity /
vector_dim / max_batch_size) records model identity for post-MVP
re-embedding migration tooling per spec/components/embedder.md §Info 캐시 +
D30. Automatic model-change detection is deferred.

client.Info() now delegates to c.info.Get() (was zero stub).
EmbedSingle wraps runedv1.RunedServiceClient.Embed in the D7 retry
schedule [0, 500ms, 2s] (retry.go).

EmbedBatch splits inputs that exceed Info.MaxBatchSize into chunks and
submits each via embedBatchOnce (each chunk gets its own retry budget).
Empty texts returns nil. Order is preserved across chunks.

embedBatchOnce verifies len(resp.Embeddings) == len(texts) — defensive
against a daemon contract violation that would otherwise misalign
results with their inputs (silent corruption).
Health() calls runedv1.RunedServiceClient.Health and translates the
proto enum HealthResponse_Status into the 'STATUS_'-stripped string
documented by the adapter contract (OK / LOADING / DEGRADED /
SHUTTING_DOWN / UNSPECIFIED).

Health is NOT retried (D8 — first embed call drives connectivity; this
RPC backs the diagnostic surface only). Callers (service/lifecycle.go)
classify LOADING vs DEGRADED downstream.
12 test functions / 17 subtest cases against an in-process bufconn-backed
RunedServiceServer:
  - EmbedSingle: happy path, retry-then-success on Unavailable,
    non-retryable error returns immediately, retries-exhausted (3 attempts),
    respects cancelled context
  - EmbedBatch: no-split when len ≤ MaxBatchSize, splits 9-into-(4+4+1)
    when above MaxBatchSize=4, resp count mismatch errors out, empty
    input no-op
  - Info: sync.Once caches across 5 calls (1 RPC), cached error path
    (1 RPC for two failed calls)
  - Health: 5-case enum→string matrix (OK/LOADING/DEGRADED/SHUTTING_DOWN/
    UNSPECIFIED), uptime/total_requests pass-through

Adds NewBufconnClient(*grpc.ClientConn) Client constructor (factored
through newWithConn) so tests can inject a bufconn-backed conn without
a unix socket. Useful in production too for callers that pool conns.

Notes:
- Two retry tests (RetryThenSuccess: 0.5s, RetriesExhaust: 2.5s) wait on
  the real D7 backoff schedule [0, 500ms, 2s]. Total test suite ~3.5s.
- Tests use status.FromError + codes.* for assertions; doesn't depend on
  any embedder/errors.go (which doesn't exist yet — see runed audit P2.4).

go test ./internal/adapters/embedder/...  ok
13 direct spec-parity items (5 RPCs, batch split, retry, info cache, slog
breadcrumb, resp count guard, status enum mapping). 3 acceptable
divergences (raw stub vs runed/client wrapper, no Health retry, no
Shutdown call). 4 open follow-up gaps (vector_dim verification,
Health-aware classification, typed error sentinels, socket path resolver).

No Python equivalent — D30 designates runed as a Go-native sibling;
audit baseline is docs/v04/spec/components/embedder.md and the runed
proto contract.
@couragehong
couragehong force-pushed the couragehong/feat/runed-grpc branch from ecce24e to 3c7a140 Compare May 7, 2026 03:23
@couragehong
couragehong merged commit 8f44b69 into feat/go-migration May 7, 2026
1 check passed
@couragehong
couragehong deleted the couragehong/feat/runed-grpc branch May 7, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant