Skip to content

Commit 8ec4883

Browse files
committed
fix: ignore truncated Gemini responses
Occasionally Gemini truncates the response on the `class=` sequence. These changes ignore such responses to reduce noise from the results.
1 parent 4fc8659 commit 8ec4883

File tree

6 files changed

+43
-6
lines changed

6 files changed

+43
-6
lines changed

runner/codegen/genkit/genkit-runner.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export class GenkitRunner implements LlmRunner {
4545
async generateConstrained<T extends z.ZodTypeAny = z.ZodTypeAny>(
4646
options: LlmConstrainedOutputGenerateRequestOptions<T>
4747
): Promise<LlmConstrainedOutputGenerateResponse<T>> {
48-
const result = await this._genkitRequest(options);
48+
const { provider, model } = this.resolveModel(options.model);
49+
const result = await this._genkitRequest(provider, model, options);
4950

5051
return {
5152
output: result.output,
@@ -72,10 +73,16 @@ export class GenkitRunner implements LlmRunner {
7273
}),
7374
};
7475

75-
const result = await this._genkitRequest(requestOptions);
76+
const { provider, model } = this.resolveModel(options.model);
77+
const result = await this._genkitRequest(provider, model, requestOptions);
78+
const files = result.output.outputFiles || [];
79+
80+
if (!provider.validateGeneratedFiles(files)) {
81+
throw new Error(`Invalid files generated by model "${options.model}"`);
82+
}
7683

7784
return {
78-
files: result.output.outputFiles || [],
85+
files,
7986
usage: result.usage,
8087
reasoning: result.reasoning,
8188
toolLogs: this.flushToolLogs(),
@@ -89,7 +96,8 @@ export class GenkitRunner implements LlmRunner {
8996
async generateText(
9097
options: LlmGenerateTextRequestOptions
9198
): Promise<LlmGenerateTextResponse> {
92-
const result = await this._genkitRequest(options);
99+
const { provider, model } = this.resolveModel(options.model);
100+
const result = await this._genkitRequest(provider, model, options);
93101

94102
return {
95103
text: result.text,
@@ -104,12 +112,12 @@ export class GenkitRunner implements LlmRunner {
104112
}
105113

106114
private async _genkitRequest(
115+
provider: GenkitModelProvider,
116+
model: ModelReference<any>,
107117
options:
108118
| LlmGenerateTextRequestOptions
109119
| LlmConstrainedOutputGenerateRequestOptions
110120
) {
111-
const { provider, model } = this.resolveModel(options.model);
112-
113121
return await rateLimitLLMRequest(
114122
provider,
115123
model,

runner/codegen/genkit/model-provider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ModelReference } from 'genkit';
22
import { GenkitPlugin, GenkitPluginV2 } from 'genkit/plugin';
33
import { RateLimiter } from 'limiter';
44
import { PromptDataMessage } from '../llm-runner.js';
5+
import { LlmResponseFile } from '../../shared-interfaces.js';
56

67
export interface RateLimitConfig {
78
requestPerMinute: RateLimiter;
@@ -46,6 +47,12 @@ export abstract class GenkitModelProvider {
4647
return key ? this.pluginFactory(key) : null;
4748
}
4849

50+
/**
51+
* Checks whether files generated by the LLM are valid.
52+
* If not, the Genkit runner will throw an error.
53+
*/
54+
abstract validateGeneratedFiles(files: LlmResponseFile[]): boolean;
55+
4956
protected abstract pluginFactory(
5057
apiKey: string
5158
): GenkitPlugin | GenkitPluginV2;

runner/codegen/genkit/providers/claude.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export class ClaudeModelProvider extends GenkitModelProvider {
3838
return {};
3939
}
4040

41+
validateGeneratedFiles(): boolean {
42+
return true;
43+
}
44+
4145
private anthropicApi = lazy(() => {
4246
return new Anthropic({ apiKey: this.getApiKey() || undefined });
4347
});

runner/codegen/genkit/providers/gemini.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { lazy } from '../../../utils/lazy-creation.js';
99
import { GoogleGenAI, Part } from '@google/genai';
1010
import { RateLimiter } from 'limiter';
11+
import { LlmResponseFile } from '../../../shared-interfaces.js';
1112

1213
export class GeminiModelProvider extends GenkitModelProvider {
1314
readonly apiKeyVariableName = 'GEMINI_API_KEY';
@@ -74,6 +75,15 @@ export class GeminiModelProvider extends GenkitModelProvider {
7475
return { thinkingConfig: { includeThoughts: opts.includeThoughts } };
7576
}
7677

78+
validateGeneratedFiles(files: LlmResponseFile[]): boolean {
79+
// Gemini responses occasionally get truncated on `class=`.
80+
// Consider these cases as invalid so they don't influence the results.
81+
return (
82+
files.length === 0 ||
83+
!files.some((file) => file.code.trim().endsWith('class='))
84+
);
85+
}
86+
7787
private async countGeminiTokens(
7888
prompt: PromptDataForCounting,
7989
modelName: string

runner/codegen/genkit/providers/grok.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ export class GrokModelProvider extends GenkitModelProvider {
9696
return {};
9797
}
9898

99+
validateGeneratedFiles(): boolean {
100+
return true;
101+
}
102+
99103
private genkitPromptToXaiFormat(
100104
prompt: PromptDataForCounting
101105
): Array<{ role: string; content: string }> {

runner/codegen/genkit/providers/open-ai.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export class OpenAiModelProvider extends GenkitModelProvider {
8181
return {};
8282
}
8383

84+
validateGeneratedFiles(): boolean {
85+
return true;
86+
}
87+
8488
private genkitPromptToOpenAi(
8589
prompt: PromptDataForCounting
8690
): Array<{ role: string; content: string }> {

0 commit comments

Comments
 (0)