Skip to content

feat: replace hand-rolled embedding providers with pgedge-go-llm-lib (PR 2 of 4)#173

Open
dpage wants to merge 5 commits into
feat/llm-lib-pr1-chat-clientsfrom
feat/llm-lib-pr2-embeddings
Open

feat: replace hand-rolled embedding providers with pgedge-go-llm-lib (PR 2 of 4)#173
dpage wants to merge 5 commits into
feat/llm-lib-pr1-chat-clientsfrom
feat/llm-lib-pr2-embeddings

Conversation

@dpage

@dpage dpage commented May 27, 2026

Copy link
Copy Markdown
Member

Summary

  • Replaces hand-rolled Voyage, OpenAI, and Ollama embedding providers in internal/embedding/ with a thin wrapper around pgedge-go-llm-lib.
  • embedding.Provider interface and NewProvider factory preserved; tool consumers unchanged.
  • ~1100 lines of provider-specific HTTP code removed (8 files deleted).
  • internal/chat/llm_tracing.go (added in PR 1) gains a local LogLevel type so the chat package no longer imports embedding.

This is PR 2 of 4. Based on PR 1 (#172). Will need rebase onto main after PR 1 lands.

Notable behaviour change

  • Provider.Dimensions() now returns 0 until the first successful Embed call (previously hard-coded per known model). The only consumer (generate_embedding) always calls Embed first, so no user-visible regression.

Test plan

  • make test passes locally.
  • Live smoke tests against real Voyage (dim=512) and OpenAI (dim=1536) APIs passed.
  • Live Ollama smoke not run (no Ollama instance in dev environment).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Embedding providers now use a shared library instead of separate custom HTTP implementations.
  • Documentation

    • Changelog updated to note the provider consolidation and lazy dimension behavior.
  • Changed

    • Embedding dimension reporting is lazily initialized from the first successful embedding and returns 0 until then.
  • New Features

    • Added runtime log-level control for LLM tracing (configurable via environment variable).

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b93db8b9-8ff1-4141-ad60-12dd59c5715a

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4cc7f and 2dd224a.

📒 Files selected for processing (15)
  • docs/changelog.md
  • internal/chat/llm_loglevel.go
  • internal/chat/llm_tracing.go
  • internal/chat/llm_tracing_test.go
  • internal/embedding/libprovider.go
  • internal/embedding/libprovider_test.go
  • internal/embedding/logger.go
  • internal/embedding/logger_test.go
  • internal/embedding/ollama.go
  • internal/embedding/ollama_test.go
  • internal/embedding/openai.go
  • internal/embedding/openai_test.go
  • internal/embedding/provider.go
  • internal/embedding/voyage.go
  • internal/embedding/voyage_test.go
💤 Files with no reviewable changes (9)
  • internal/embedding/provider.go
  • internal/embedding/ollama.go
  • internal/embedding/openai.go
  • internal/embedding/ollama_test.go
  • internal/embedding/voyage.go
  • internal/embedding/openai_test.go
  • internal/embedding/logger_test.go
  • internal/embedding/voyage_test.go
  • internal/embedding/logger.go
✅ Files skipped from review due to trivial changes (1)
  • docs/changelog.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/chat/llm_tracing_test.go
  • internal/embedding/libprovider_test.go
  • internal/chat/llm_loglevel.go
  • internal/chat/llm_tracing.go
  • internal/embedding/libprovider.go

Walkthrough

This PR consolidates embedding provider clients (Voyage, OpenAI, Ollama) to use the shared pgedge-go-llm-lib library. Log-level management moves from the embedding package to the chat package. Provider.Dimensions() is now lazily populated (returns 0 until the first successful Embed), then cached.

Changes

Embedding Provider Library Consolidation

Layer / File(s) Summary
Chat package log-level system
internal/chat/llm_loglevel.go, internal/chat/llm_tracing.go, internal/chat/llm_tracing_test.go
New LogLevel enum and atomic global state in chat, initialized from PGEDGE_LLM_LOG_LEVEL; tracing now consults chat log level; tests updated to use chat helpers.
New libProvider implementation
internal/embedding/libprovider.go
libProvider wraps pgedge-go-llm-lib, maps Configllm.Options, validates keys/defaults, constructs llm.Client, delegates Embed, and lazily caches embedding dimension; exposes Dimensions, ModelName, and ProviderName.
LibProvider test coverage
internal/embedding/libprovider_test.go
Tests using mocked HTTP servers for Voyage/OpenAI/Ollama validating round-trip embeddings, dimension reporting, lazy initialization (0 before first embed), and error propagation.
Provider factory cleanup
internal/embedding/provider.go
Removed old exported NewProvider factory and unused fmt import; legacy provider files and embedding logger removed elsewhere.
Changelog update
docs/changelog.md
Documented migration to pgedge-go-llm-lib and lazy Provider.Dimensions() behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant libProvider
  participant llmClient as llm.Client
  participant Upstream

  Caller->>libProvider: NewProvider(cfg)
  libProvider->>llmClient: llm.NewClient(options)
  Caller->>libProvider: Embed(ctx, text)
  libProvider->>llmClient: Embed(ctx, text)
  llmClient->>Upstream: provider-specific embed request
  Upstream-->>llmClient: embedding response []float64
  llmClient-->>libProvider: embedding []float64
  libProvider->>libProvider: set cached dimension if 0
  libProvider-->>Caller: embedding []float64
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • pgEdge/pgedge-postgres-mcp#32: Prior PR that modified provider implementations for configurable baseURL handling and is related to replacing old providers with a lib-backed provider.

Suggested reviewers

  • mmols
  • tsivaprasad
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main objective: replacing hand-rolled embedding providers with pgedge-go-llm-lib. It is specific, concise, and clearly reflects the primary change across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/llm-lib-pr2-embeddings

Comment @coderabbitai help to get the list of available commands and usage tips.

@dpage

dpage commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codacy-production

codacy-production Bot commented May 27, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 1 medium

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 48 complexity · 2 duplication

Metric Results
Complexity 48
Duplication 2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@dpage dpage force-pushed the feat/llm-lib-pr2-embeddings branch from a264223 to b117629 Compare May 27, 2026 16:46
@dpage

dpage commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/chat/llm_tracing.go (1)

27-30: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update stale package reference in tracing comment.

The comment still says tracing is controlled by the embedding-package log level, but this code now uses the chat package log level. Please update this wording to avoid confusion during maintenance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/chat/llm_tracing.go` around lines 27 - 30, Update the stale comment
for tracingRoundTripper to say that request/response trace logging is controlled
by the chat package log level (not the embedding-package log level); locate the
comment above the tracingRoundTripper type in llm_tracing.go and replace the
phrase "embedding-package log level" with "chat package log level" and adjust
wording to state that trace logging occurs when the chat package log level is
Debug or Trace so the comment accurately reflects current behavior.
🧹 Nitpick comments (2)
internal/embedding/libprovider_test.go (1)

144-147: ⚡ Quick win

Assert Embed error in lazy-dimensions test.

The test currently discards the Embed error, which can hide the real failure source. Failing fast on error will make this test much easier to diagnose.

Suggested diff
-	_, _ = p.Embed(context.Background(), "x")
+	if _, err := p.Embed(context.Background(), "x"); err != nil {
+		t.Fatalf("Embed: %v", err)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/embedding/libprovider_test.go` around lines 144 - 147, The test is
discarding the error from p.Embed which can mask failures; update the test in
libprovider_test.go to capture the returned error from p.Embed (e.g., err :=
p.Embed(...)) and fail the test immediately if err != nil (use t.Fatalf or
t.Fatal with the error), before asserting p.Dimensions() == 8, referencing the
Embed method and Dimensions() on p.
internal/embedding/libprovider.go (1)

112-114: ⚡ Quick win

Use CAS to enforce one-time dimension initialization.

Load()==0 followed by Store() is racy for “first successful Embed wins” semantics under concurrent calls. CompareAndSwap(0, d) makes the intent explicit and lock-free.

Suggested diff
-	if d := int32(len(vec)); d > 0 && p.dim.Load() == 0 {
-		p.dim.Store(d)
-	}
+	if d := int32(len(vec)); d > 0 {
+		p.dim.CompareAndSwap(0, d)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/embedding/libprovider.go` around lines 112 - 114, The current
one-time dimension init uses p.dim.Load() == 0 followed by p.dim.Store(d) which
is racy; replace that logic with an atomic CompareAndSwap so only the first
successful embed sets the dimension. Locate the block where d := int32(len(vec))
and p.dim.Load()/Store() are used (in libprovider.go) and change it to attempt
p.dim.CompareAndSwap(0, d) so the first goroutine wins and others won't
overwrite the stored dimension.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/chat/llm_tracing.go`:
- Around line 27-30: Update the stale comment for tracingRoundTripper to say
that request/response trace logging is controlled by the chat package log level
(not the embedding-package log level); locate the comment above the
tracingRoundTripper type in llm_tracing.go and replace the phrase
"embedding-package log level" with "chat package log level" and adjust wording
to state that trace logging occurs when the chat package log level is Debug or
Trace so the comment accurately reflects current behavior.

---

Nitpick comments:
In `@internal/embedding/libprovider_test.go`:
- Around line 144-147: The test is discarding the error from p.Embed which can
mask failures; update the test in libprovider_test.go to capture the returned
error from p.Embed (e.g., err := p.Embed(...)) and fail the test immediately if
err != nil (use t.Fatalf or t.Fatal with the error), before asserting
p.Dimensions() == 8, referencing the Embed method and Dimensions() on p.

In `@internal/embedding/libprovider.go`:
- Around line 112-114: The current one-time dimension init uses p.dim.Load() ==
0 followed by p.dim.Store(d) which is racy; replace that logic with an atomic
CompareAndSwap so only the first successful embed sets the dimension. Locate the
block where d := int32(len(vec)) and p.dim.Load()/Store() are used (in
libprovider.go) and change it to attempt p.dim.CompareAndSwap(0, d) so the first
goroutine wins and others won't overwrite the stored dimension.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 644484c4-3ccb-476b-9b1b-53f550093979

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee4f88 and b117629.

📒 Files selected for processing (15)
  • docs/changelog.md
  • internal/chat/llm_loglevel.go
  • internal/chat/llm_tracing.go
  • internal/chat/llm_tracing_test.go
  • internal/embedding/libprovider.go
  • internal/embedding/libprovider_test.go
  • internal/embedding/logger.go
  • internal/embedding/logger_test.go
  • internal/embedding/ollama.go
  • internal/embedding/ollama_test.go
  • internal/embedding/openai.go
  • internal/embedding/openai_test.go
  • internal/embedding/provider.go
  • internal/embedding/voyage.go
  • internal/embedding/voyage_test.go
💤 Files with no reviewable changes (9)
  • internal/embedding/openai.go
  • internal/embedding/voyage_test.go
  • internal/embedding/ollama.go
  • internal/embedding/logger_test.go
  • internal/embedding/ollama_test.go
  • internal/embedding/logger.go
  • internal/embedding/openai_test.go
  • internal/embedding/voyage.go
  • internal/embedding/provider.go

dpage added a commit that referenced this pull request May 27, 2026
- Use atomic CompareAndSwap for one-time dimension caching to remove
  the Load+Store race window when concurrent Embed calls complete.
- Fail-fast on Embed error in the lazy-dimensions test so a wrapper
  regression surfaces clearly instead of being hidden.
- Update stale comment on tracingRoundTripper: the log level now
  lives in the chat package, not embedding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dpage

dpage commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

dpage and others added 5 commits May 28, 2026 09:16
Implements the existing Provider interface against the shared
library. NewProvider routes Voyage / OpenAI / Ollama through the
wrapper. The old NewProvider is renamed to newProviderOLD in this
commit; the old per-provider implementations and the OLD factory
are deleted in a later commit of this PR.

Dimensions() is lazily populated from the first successful Embed
call since the library does not expose vector size up front.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves the LogLevel type and PGEDGE_LLM_LOG_LEVEL env reader into the
chat package. Decouples llm_tracing.go from internal/embedding ahead
of the embedding package's deletion in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All three providers now go through pgedge-go-llm-lib via the
libProvider wrapper. ~1100 lines of provider HTTP code removed; the
embedding package now contains only the Provider interface, Config
struct, libProvider wrapper, and NewProvider factory.

The logger.go file is also deleted; chat package's llm_tracing.go now
owns its own log level (moved in the previous commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Use atomic CompareAndSwap for one-time dimension caching to remove
  the Load+Store race window when concurrent Embed calls complete.
- Fail-fast on Embed error in the lazy-dimensions test so a wrapper
  regression surfaces clearly instead of being hidden.
- Update stale comment on tracingRoundTripper: the log level now
  lives in the chat package, not embedding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dpage dpage force-pushed the feat/llm-lib-pr2-embeddings branch from 7a4cc7f to 2dd224a Compare May 28, 2026 08:17
@dpage

dpage commented May 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dpage

dpage commented May 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dpage dpage marked this pull request as ready for review May 28, 2026 08:53
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