Skip to content

Commit 816f634

Browse files
committed
fix: address PR review feedback for Gemini tools feature
- Fix Hindi translation grammatical error in settings.json - Internationalize 'Sources:' string and error messages in gemini.ts - Add comprehensive error scenario tests to gemini-handler.spec.ts - Remove unused currentModelId prop from Gemini component - Update all locale files with new translation keys
1 parent 8fad431 commit 816f634

File tree

23 files changed

+141
-27
lines changed

23 files changed

+141
-27
lines changed

src/api/providers/__tests__/gemini-handler.spec.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, it, expect, vi } from "vitest"
2+
import { t } from "i18next"
23
import { GeminiHandler } from "../gemini"
34
import type { ApiHandlerOptions } from "../../../shared/api"
45

@@ -33,4 +34,104 @@ describe("GeminiHandler backend support", () => {
3334
const promptConfig = stub.mock.calls[0][0].config
3435
expect(promptConfig.tools).toBeUndefined()
3536
})
37+
38+
describe("error scenarios", () => {
39+
it("should handle grounding metadata extraction failure gracefully", async () => {
40+
const options = {
41+
apiProvider: "gemini",
42+
enableGrounding: true,
43+
} as ApiHandlerOptions
44+
const handler = new GeminiHandler(options)
45+
46+
const mockStream = async function* () {
47+
yield {
48+
candidates: [
49+
{
50+
groundingMetadata: {
51+
// Invalid structure - missing groundingChunks
52+
},
53+
content: { parts: [{ text: "test response" }] },
54+
},
55+
],
56+
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5 },
57+
}
58+
}
59+
60+
const stub = vi.fn().mockReturnValue(mockStream())
61+
// @ts-ignore access private client
62+
handler["client"].models.generateContentStream = stub
63+
64+
const messages = []
65+
for await (const chunk of handler.createMessage("test", [] as any)) {
66+
messages.push(chunk)
67+
}
68+
69+
// Should still return the main content without sources
70+
expect(messages.some((m) => m.type === "text" && m.text === "test response")).toBe(true)
71+
expect(messages.some((m) => m.type === "text" && m.text?.includes("Sources:"))).toBe(false)
72+
})
73+
74+
it("should handle malformed grounding metadata", async () => {
75+
const options = {
76+
apiProvider: "gemini",
77+
enableGrounding: true,
78+
} as ApiHandlerOptions
79+
const handler = new GeminiHandler(options)
80+
81+
const mockStream = async function* () {
82+
yield {
83+
candidates: [
84+
{
85+
groundingMetadata: {
86+
groundingChunks: [
87+
{ web: null }, // Missing URI
88+
{ web: { uri: "https://example.com" } }, // Valid
89+
{}, // Missing web property entirely
90+
],
91+
},
92+
content: { parts: [{ text: "test response" }] },
93+
},
94+
],
95+
usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 5 },
96+
}
97+
}
98+
99+
const stub = vi.fn().mockReturnValue(mockStream())
100+
// @ts-ignore access private client
101+
handler["client"].models.generateContentStream = stub
102+
103+
const messages = []
104+
for await (const chunk of handler.createMessage("test", [] as any)) {
105+
messages.push(chunk)
106+
}
107+
108+
// Should only include valid citations
109+
const sourceMessage = messages.find((m) => m.type === "text" && m.text?.includes("[2]"))
110+
expect(sourceMessage).toBeDefined()
111+
if (sourceMessage && "text" in sourceMessage) {
112+
expect(sourceMessage.text).toContain("https://example.com")
113+
expect(sourceMessage.text).not.toContain("[1]")
114+
expect(sourceMessage.text).not.toContain("[3]")
115+
}
116+
})
117+
118+
it("should handle API errors when tools are enabled", async () => {
119+
const options = {
120+
apiProvider: "gemini",
121+
enableUrlContext: true,
122+
enableGrounding: true,
123+
} as ApiHandlerOptions
124+
const handler = new GeminiHandler(options)
125+
126+
const mockError = new Error("API rate limit exceeded")
127+
const stub = vi.fn().mockRejectedValue(mockError)
128+
// @ts-ignore access private client
129+
handler["client"].models.generateContentStream = stub
130+
131+
await expect(async () => {
132+
const generator = handler.createMessage("test", [] as any)
133+
await generator.next()
134+
}).rejects.toThrow(t("common:errors.gemini.generate_stream", { error: "API rate limit exceeded" }))
135+
})
136+
})
36137
})

src/api/providers/gemini.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
134134
if (pendingGroundingMetadata) {
135135
const citations = this.extractCitationsOnly(pendingGroundingMetadata)
136136
if (citations) {
137-
yield { type: "text", text: `\n\nSources: ${citations}` }
137+
yield { type: "text", text: `\n\n${t("common:errors.gemini.sources")} ${citations}` }
138138
}
139139
}
140140

@@ -230,7 +230,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
230230
if (candidate?.groundingMetadata) {
231231
const citations = this.extractCitationsOnly(candidate.groundingMetadata)
232232
if (citations) {
233-
text += `\n\nSources: ${citations}`
233+
text += `\n\n${t("common:errors.gemini.sources")} ${citations}`
234234
}
235235
}
236236

src/i18n/locales/ca/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/de/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/en/common.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@
8080
},
8181
"gemini": {
8282
"generate_stream": "Gemini generate context stream error: {{error}}",
83-
"generate_complete_prompt": "Gemini completion error: {{error}}"
83+
"generate_complete_prompt": "Gemini completion error: {{error}}",
84+
"sources": "Sources:"
8485
}
8586
},
8687
"warnings": {

src/i18n/locales/es/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/fr/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/hi/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/id/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/i18n/locales/it/common.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)