feat: add Gemini embeddings support + Letta compatibility fixes

Cherry-picked from decolua/9router#148 (author: xuandung38 / Hồ Xuân Dũng <me@hxd.vn>)

- Add Google AI (Gemini) embeddings support for /v1/embeddings endpoint
- Add Gemini embedding models: gemini-embedding-001, text-embedding-005, text-embedding-004
- Inject missing object/created fields for Letta and strict OpenAI clients
- Strip Azure-specific fields (prompt_filter_results, content_filter_results) from responses
- Fix Dockerfile: copy open-sse directory into Docker runner stage

Skipped: whitelist message field stripping (commit 3/7/8) — too aggressive for all providers
Skipped: default stream=false change (commit 9) — behavior change needs further review
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Hồ Xuân Dũng
2026-02-20 15:01:10 +07:00
committed by decolua
co-authored by Cursor
parent e1e5a81613
commit a57a8ce206
5 changed files with 154 additions and 21 deletions
+20 -1
View File
@@ -82,6 +82,25 @@ export function createSSEStream(options = {}) {
const idFixed = fixInvalidId(parsed);
// Ensure OpenAI-required fields are present on streaming chunks (Letta compat)
let fieldsInjected = false;
if (!parsed.object) { parsed.object = "chat.completion.chunk"; fieldsInjected = true; }
if (!parsed.created) { parsed.created = Math.floor(Date.now() / 1000); fieldsInjected = true; }
// Strip Azure-specific non-standard fields from streaming chunks
if (parsed.prompt_filter_results !== undefined) {
delete parsed.prompt_filter_results;
fieldsInjected = true;
}
if (parsed?.choices) {
for (const choice of parsed.choices) {
if (choice.content_filter_results !== undefined) {
delete choice.content_filter_results;
fieldsInjected = true;
}
}
}
if (!hasValuableContent(parsed, FORMATS.OPENAI)) {
continue;
}
@@ -115,7 +134,7 @@ export function createSSEStream(options = {}) {
parsed.usage = filterUsageForFormat(buffered, FORMATS.OPENAI);
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
} else if (idFixed) {
} else if (idFixed || fieldsInjected) {
output = `data: ${JSON.stringify(parsed)}\n`;
injectedUsage = true;
}