Commit 6a66a6e
authored
feat(gemini): multimodal content embedding via shared Content API (#459)
* docs(06): capture phase context
* docs(state): record phase 6 context session
* docs(06): research gemini multimodal adoption phase
* docs(phase-6): add validation strategy
* docs(06): create phase plan
* feat(06-01): add content.go with conversion helpers and capability derivation
- neutralIntentToTaskType map for 5 shared neutral intents to Gemini task types
- extToMIME map for file extension MIME inference
- capabilitiesForModel: full 5-modality caps for gemini-embedding-2-preview, text-only for others
- resolveBytes: handles bytes, base64, file, and URL source kinds
- resolveMIME: falls back from BinarySource.MIMEType to file extension
- validateMIMEModality: rejects mismatched MIME/modality combinations
- convertToGenaiContent / convertToGenaiContents: converts shared Content to genai.Content
- resolveTaskTypeForContent: ProviderHints > intent mapper > default
* feat(06-01): implement ContentEmbeddingFunction+CapabilityAware+IntentMapper on GeminiEmbeddingFunction
- DefaultEmbeddingModel updated to gemini-embedding-2-preview; LegacyEmbeddingModel added for gemini-embedding-001
- Compile-time assertions for ContentEmbeddingFunction, CapabilityAware, IntentMapper
- Client.CreateContentEmbedding: per-item task type resolution for single items, batch uses default
- GeminiEmbeddingFunction.EmbedContent and EmbedContents: validate against caps then delegate to CreateContentEmbedding
- GeminiEmbeddingFunction.Capabilities: delegates to capabilitiesForModel
- GeminiEmbeddingFunction.MapIntent: translates 5 neutral intents, rejects non-neutral with escape-hatch hint
- RegisterContent("google_genai") added to init() alongside existing RegisterDense
* docs(06-01): complete gemini multimodal content interface plan
- 06-01-SUMMARY.md: full plan summary with task commits, decisions, and patterns
- STATE.md: advanced to plan 2, recorded metrics, added key decisions
- ROADMAP.md: updated phase 6 plan progress (1/2 summaries)
- REQUIREMENTS.md: marked GEM-01, GEM-02, GEM-03 complete
* test(06-02): add unit tests for gemini multimodal content implementation
- TestCapabilitiesForModel: 3 model variants (preview, legacy, unknown)
- TestGeminiCapabilities: interface method on GeminiEmbeddingFunction
- TestMapIntent: all 5 neutral intents map to correct Gemini task types
- TestMapIntentRejectsNonNeutral: non-neutral intents rejected with ProviderHints hint
- TestResolveMIME: explicit MIME, extension fallback, error cases
- TestValidateMIMEModality: valid and invalid MIME-modality combinations
- TestResolveBytesKinds: bytes, base64, file kinds (URL skipped)
- TestConvertToGenaiContent*: text, binary, mixed, error cases
- TestResolveTaskTypeForContent: priority chain (hints > intent > default)
- TestEmbedContentLegacyModelRejectsMultimodal: D-03/D-04 negative case
- TestGeminiContentRegistration: GEM-03 HasContent + BuildContent round-trip
- TestGeminiContentConfigRoundTrip: Name + GetConfig -> BuildContent
- TestDefaultModelChanged: D-01 constant verification
* docs(06-02): complete gemini multimodal content tests plan
- 06-02-SUMMARY.md: 19-function unit test suite proving GEM-01/GEM-02/GEM-03
- STATE.md: advanced to last-plan, 16/16 plans complete, decisions recorded
- ROADMAP.md: phase 06 marked Complete (2/2 summaries)
* docs(phase-06): complete phase execution
* docs(phase-06): evolve PROJECT.md after phase completion
* test(gemini): add multimodal content integration tests and mock-based coverage
- Add testdata/ with 5 modality assets (PNG, MP3, MP4, PDF, text)
- Add gemini_content_integration_test.go (ef tag): text, image, audio,
video, PDF, mixed-part, batch, intent, ProviderHints, dimension tests
- Add mock-based unit tests for CreateContentEmbedding, EmbedContent,
EmbedContents defensive paths (empty response, API error, validation)
- Add convertToGenaiContents, resolveBytes URL, unsupported kind tests
- Trim MP3 to 60s (Gemini 80s audio limit)
- Fix lint: gci import ordering on compile-time assertions
- Coverage: 57.9% → 84.7% (unit-only: 72.9%)
* docs: add phase 8 for Gemini/Nemotron documentation
* fix(gemini): add nil source guards, structural validation, and configurable file size limit
* refactor(gemini): use genai.NewPartFromURI for URL sources instead of client-side fetch
* feat(gemini): honor Content.Dimension in single-content embedding requests
* fix(gemini): derive capabilities from effective model when context override is set
* fix(gemini): enforce MaxBatchSize on EmbedContents and default to 250
* fix(gemini): validate IntentMapper result with IsValid() for consistency
* fix(gemini): add path traversal guard, use LimitReader for file reads, validate in CreateContentEmbedding
- Reject file paths containing ".." to prevent path traversal
- Replace os.Stat+os.ReadFile with os.Open+io.LimitReader to eliminate TOCTOU race
- Add ValidateContents call in CreateContentEmbedding for defense-in-depth
* fix(gemini): reject per-item overrides in batch requests and check nil embedding values
- Error when batch contents have per-item Intent, Dimension, or ProviderHints
since Gemini applies one config per batch (prevents silent data corruption)
- Check for nil embedding Values in API response (aligns with Voyage pattern)
* fix(gemini): enforce MaxFileSize on bytes and base64 payloads
The genai SDK performs no client-side size validation. Enforce the
configurable limit (default 100MB) on all inline payload types
consistently, not just file reads.
* docs(gemini): document batch limitations for per-item Content overrides
* docs(phase-06): update validation strategy with Nyquist audit results
* fix(gemini): pre-check base64 payload size before decoding to avoid OOM allocation1 parent fce8437 commit 6a66a6e
26 files changed
Lines changed: 3930 additions & 25 deletions
File tree
- .planning
- phases
- 06-gemini-multimodal-adoption
- 08-document-gemini-and-nemotron-multimodal-embedding-functions
- pkg/embeddings
- gemini
- testdata
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
38 | | - | |
39 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
87 | | - | |
88 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| 113 | + | |
113 | 114 | | |
114 | | - | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
115 | 118 | | |
116 | 119 | | |
117 | 120 | | |
| |||
134 | 137 | | |
135 | 138 | | |
136 | 139 | | |
137 | | - | |
| 140 | + | |
138 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
| 3 | + | |
| 4 | + | |
5 | 5 | | |
6 | | - | |
7 | | - | |
| 6 | + | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
12 | | - | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
27 | | - | |
| 26 | + | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| 61 | + | |
| 62 | + | |
61 | 63 | | |
62 | 64 | | |
63 | 65 | | |
| |||
90 | 92 | | |
91 | 93 | | |
92 | 94 | | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
93 | 100 | | |
94 | 101 | | |
95 | 102 | | |
96 | 103 | | |
97 | 104 | | |
98 | 105 | | |
99 | 106 | | |
| 107 | + | |
100 | 108 | | |
101 | 109 | | |
102 | 110 | | |
| |||
122 | 130 | | |
123 | 131 | | |
124 | 132 | | |
125 | | - | |
126 | | - | |
| 133 | + | |
| 134 | + | |
127 | 135 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
0 commit comments