Skip to content

Commit 4509103

Browse files
authored
feat(embeddings): add OpenRouter provider and WithModelString for OpenAI (#477)
* docs(15): capture phase context * docs(state): record phase 15 context session * docs(15): research OpenRouter embeddings compatibility * docs(15): create phase plan for OpenRouter embeddings compatibility * docs(15): add validation strategy for phase 15 * feat(15-01): add WithModelString to OpenAI provider and update FromConfig - Add WithModelString option for arbitrary model names on OpenAI-compatible endpoints - Update NewOpenAIEmbeddingFunctionFromConfig to use WithModelString for non-standard models - Existing WithModel validation unchanged for known constants * feat(15-01): add standalone OpenRouter embedding provider package - Create openrouter package with Client, request/response types, and HTTP implementation - Add ProviderPreferences struct with custom MarshalJSON and Extras field - Add functional options: WithModel, WithEncodingFormat, WithInputType, WithProviderPreferences - Implement EmbeddingFunction interface with config round-trip support - Register as "openrouter" in dense embedding registry * docs(15-01): complete OpenRouter provider and WithModelString plan - Add 15-01-SUMMARY.md with execution results - Update STATE.md with phase 15 progress and decisions - Update ROADMAP.md with plan progress * test(15-02): add WithModelString and config round-trip tests for OpenAI - Test WithModelString accepts arbitrary model strings via httptest - Test WithModelString rejects empty string - Test config round-trip with non-standard model name * test(15-02): add comprehensive OpenRouter provider unit tests - Request serialization test for encoding_format, input_type, provider fields - ProviderPreferences MarshalJSON: typed-only, extras-only, merge precedence - Config round-trip with all fields including nested provider preferences - EmbedQuery dispatches single string input - Empty model rejected by validation - Name returns "openrouter" - Registry registration verified * docs(15-02): complete OpenRouter test coverage plan - SUMMARY.md with test coverage details - STATE.md updated to phase 15 plan 02 complete - ROADMAP.md phase 15 marked complete - Requirements OR-01 through OR-05 marked complete * docs(phase-15): complete phase execution * docs(phase-15): evolve PROJECT.md after phase completion * test(15): complete UAT - 7 passed, 0 issues * fix(openrouter): harden provider config and response validation * test(openrouter): cover validation and fallback paths * test(openrouter): add integration tests for OpenRouter embedding provider * fix(openrouter): add base64 embedding decoding and docs - Add custom UnmarshalJSON on EmbeddingData to handle both float and base64-encoded embedding responses (IEEE 754 little-endian float32) - Fix integration test using assert.Failf before t.Skip on missing .env - Add OpenRouter provider documentation to README, embeddings.md, and go-examples embedding-functions.md * fix(openrouter): truncate raw error body in API error messages Cap unstructured error responses at 512 characters to prevent sensitive data from upstream proxies leaking into error messages. Refs #478 * fix(openrouter): use rune-based truncation in parseAPIError Slice by runes instead of bytes to avoid splitting multi-byte UTF-8 characters when truncating error response bodies.
1 parent 958be08 commit 4509103

25 files changed

Lines changed: 3224 additions & 17 deletions

.planning/PROJECT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@ Go applications can use Chroma and embedding providers through a stable, portabl
7171
| Pivot Phase 7 from vLLM/Nemotron to VoyageAI | vLLM lacks NVOmniEmbedModel support; VoyageAI multimodal validates portability with text/image/video | ✓ Good |
7272

7373
---
74-
*Last updated: 2026-03-29 — Phase 14 (delete-with-limit) complete: added Limit parameter to Collection.Delete with validation, embedded path wiring, and comprehensive test coverage.*
74+
*Last updated: 2026-03-30 — Phase 15 (openrouter-embeddings-compatibility) complete: standalone OpenRouter embedding provider with ProviderPreferences, encoding_format, input_type fields; OpenAI WithModelString for proxy endpoints; full test coverage.*

.planning/REQUIREMENTS.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@
8484
- [x] **DEL-04**: Embedded path converts `*int32` limit to `*uint32` and passes to `EmbeddedDeleteRecordsRequest.Limit`
8585
- [x] **DEL-05**: Tests cover option application, validation edge cases, and HTTP serialization round-trip
8686

87+
### OpenRouter Embeddings Compatibility
88+
89+
- [x] **OR-01**: `CreateEmbeddingRequest` in the OpenRouter package supports `encoding_format`, `input_type`, and `provider` fields
90+
- [x] **OR-02**: OpenAI provider's `WithModelString` accepts any non-empty string without validation for use with compatible proxies
91+
- [x] **OR-03**: `ProviderPreferences` is a typed struct with all documented OpenRouter fields plus `Extras map[string]any` with custom `MarshalJSON`
92+
- [x] **OR-04**: Existing OpenAI behavior and tests remain unchanged
93+
- [x] **OR-05**: OpenRouter provider registered as `"openrouter"` in dense registry with full `GetConfig`/`FromConfig` config round-trip
94+
8795
## v2 Requirements
8896

8997
### Provider Adoption
@@ -155,12 +163,17 @@
155163
| DEL-03 | Phase 14 | Complete |
156164
| DEL-04 | Phase 14 | Complete |
157165
| DEL-05 | Phase 14 | Planned |
166+
| OR-01 | Phase 15 | Planned |
167+
| OR-02 | Phase 15 | Planned |
168+
| OR-03 | Phase 15 | Planned |
169+
| OR-04 | Phase 15 | Planned |
170+
| OR-05 | Phase 15 | Planned |
158171

159172
**Coverage:**
160-
- v1 requirements: 46 total
161-
- Mapped to phases: 46
173+
- v1 requirements: 51 total
174+
- Mapped to phases: 51
162175
- Unmapped: 0
163176

164177
---
165178
*Requirements defined: 2026-03-18*
166-
*Last updated: 2026-03-29 -- added DEL-01/02/03/04/05 for phase 14*
179+
*Last updated: 2026-03-30 -- added OR-01/02/03/04/05 for phase 15*

.planning/ROADMAP.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ This roadmap initializes GSD planning for the current brownfield milestone focus
2828
- [x] **Phase 12: SDK Auto-Wiring Research** - Trace contentEmbeddingFunction auto-wiring behavior in official Chroma SDKs. (issue #455) (completed 2026-03-28)
2929
- [x] **Phase 13: Collection.ForkCount** - Add ForkCount endpoint support for upstream /fork_count API. (issue #460) (completed 2026-03-28)
3030
- [x] **Phase 14: Delete with Limit** - Add delete-with-limit support for upstream limit parameter. (issue #439) [1/2 plans complete] (completed 2026-03-29)
31-
- [ ] **Phase 15: OpenRouter Embeddings Compatibility** - Add first-class OpenRouter support via provider preferences and encoding_format. (issue #438)
31+
- [x] **Phase 15: OpenRouter Embeddings Compatibility** - Add first-class OpenRouter support via provider preferences and encoding_format. (issue #438) (completed 2026-03-30)
3232
- [ ] **Phase 16: Twelve Labs Embedding Function** - Add Twelve Labs multimodal embedding provider. (issue #190)
3333
- [ ] **Phase 17: Cloud RRF and GroupBy Test Coverage** - Add cloud integration tests for Search API RRF and GroupBy primitives. (issue #462)
3434
- [ ] **Phase 18: Embedded Client contentEmbeddingFunction Parity** - Add contentEmbeddingFunction support to embeddedCollection for feature parity with HTTP client. (issue #472)
@@ -179,7 +179,7 @@ Plans:
179179
| 12. SDK Auto-Wiring Research | 1/1 | Complete | 2026-03-28 |
180180
| 13. Collection.ForkCount | 2/2 | Complete | 2026-03-28 |
181181
| 14. Delete with Limit | 2/2 | Complete | 2026-03-29 |
182-
| 15. OpenRouter Embeddings | 0/0 | Not started | - |
182+
| 15. OpenRouter Embeddings | 2/2 | Complete | 2026-03-30 |
183183
| 16. Twelve Labs EF | 0/0 | Not started | - |
184184
| 17. Cloud RRF/GroupBy Tests | 0/0 | Not started | - |
185185
| 18. Embedded contentEF Parity | 0/0 | Not started | - |
@@ -287,10 +287,11 @@ Plans:
287287
3. Provider preferences struct covers documented OpenRouter fields with extensibility.
288288
4. Existing OpenAI behavior and tests remain unchanged.
289289
5. Docs include OpenRouter usage example with `WithBaseURL`.
290-
**Plans:** 0 plans
290+
**Plans:** 2/2 plans complete
291291

292292
Plans:
293-
- [ ] TBD (run /gsd:plan-phase 15 to break down)
293+
- [x] 15-01-PLAN.md -- Add WithModelString to OpenAI provider and create standalone OpenRouter provider package
294+
- [x] 15-02-PLAN.md -- Add unit tests and integration verification for OpenRouter provider
294295

295296
### Phase 16: Twelve Labs Embedding Function
296297
**Goal:** Add a new Twelve Labs multimodal embedding provider supporting text, image, and audio embeddings via the Twelve Labs API.

.planning/STATE.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ gsd_state_version: 1.0
33
milestone: v0.4.1
44
milestone_name: Provider-Neutral Multimodal Foundations
55
status: Ready to plan
6-
stopped_at: Completed 14-02-PLAN.md
7-
last_updated: "2026-03-29T18:54:40.196Z"
6+
stopped_at: Completed 15-02-PLAN.md
7+
last_updated: "2026-03-30T18:42:28.836Z"
88
progress:
99
total_phases: 18
10-
completed_phases: 14
11-
total_plans: 31
12-
completed_plans: 31
10+
completed_phases: 15
11+
total_plans: 33
12+
completed_plans: 33
1313
---
1414

1515
# Project State
@@ -23,7 +23,7 @@ See: .planning/PROJECT.md (updated 2026-03-18)
2323

2424
## Current Position
2525

26-
Phase: 15
26+
Phase: 16
2727
Plan: Not started
2828

2929
## Performance Metrics
@@ -74,6 +74,8 @@ Plan: Not started
7474
| Phase 13 P02 | 2min | 2 tasks | 2 files |
7575
| Phase 14 P01 | 4min | 2 tasks | 3 files |
7676
| Phase 14 P02 | 2min | 2 tasks | 2 files |
77+
| Phase 15 P01 | 2min | 2 tasks | 5 files |
78+
| Phase 15 P02 | 2min | 2 tasks | 2 files |
7779

7880
## Accumulated Context
7981

@@ -127,6 +129,8 @@ Recent decisions affecting current work:
127129
- [Phase 11]: Test ownership gating via direct struct construction - no server required
128130
- [Phase 13]: Follow IndexingStatus GET+JSON pattern for ForkCount HTTP implementation
129131
- [Phase 13]: Use run() pattern in fork_count example to satisfy gocritic exitAfterDefer lint rule
132+
- [Phase 15]: Follow Together provider pattern for standalone OpenRouter package - no dependency on openai package
133+
- [Phase 15]: WithModelString bypasses validation for proxy-compatible model names while WithModel retains strict validation
130134

131135
### Roadmap Evolution
132136

@@ -159,6 +163,6 @@ None.
159163

160164
## Session
161165

162-
**Last Date:** 2026-03-29T18:50:49.649Z
163-
**Stopped At:** Completed 14-02-PLAN.md
166+
**Last Date:** 2026-03-30T18:37:14Z
167+
**Stopped At:** Completed 15-02-PLAN.md
164168
**Resume File:** None

0 commit comments

Comments
 (0)