Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions runner/codegen/genkit/genkit-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export class GenkitRunner implements LlmRunner {
async generateConstrained<T extends z.ZodTypeAny = z.ZodTypeAny>(
options: LlmConstrainedOutputGenerateRequestOptions<T>
): Promise<LlmConstrainedOutputGenerateResponse<T>> {
const result = await this._genkitRequest(options);
const { provider, model } = this.resolveModel(options.model);
const result = await this._genkitRequest(provider, model, options);

return {
output: result.output,
Expand All @@ -72,10 +73,16 @@ export class GenkitRunner implements LlmRunner {
}),
};

const result = await this._genkitRequest(requestOptions);
const { provider, model } = this.resolveModel(options.model);
const result = await this._genkitRequest(provider, model, requestOptions);
const files = result.output.outputFiles || [];

if (!provider.validateGeneratedFiles(files)) {
throw new Error(`Invalid files generated by model "${options.model}"`);
}

return {
files: result.output.outputFiles || [],
files,
usage: result.usage,
reasoning: result.reasoning,
toolLogs: this.flushToolLogs(),
Expand All @@ -89,7 +96,8 @@ export class GenkitRunner implements LlmRunner {
async generateText(
options: LlmGenerateTextRequestOptions
): Promise<LlmGenerateTextResponse> {
const result = await this._genkitRequest(options);
const { provider, model } = this.resolveModel(options.model);
const result = await this._genkitRequest(provider, model, options);

return {
text: result.text,
Expand All @@ -104,12 +112,12 @@ export class GenkitRunner implements LlmRunner {
}

private async _genkitRequest(
provider: GenkitModelProvider,
model: ModelReference<any>,
options:
| LlmGenerateTextRequestOptions
| LlmConstrainedOutputGenerateRequestOptions
) {
const { provider, model } = this.resolveModel(options.model);

return await rateLimitLLMRequest(
provider,
model,
Expand Down
7 changes: 7 additions & 0 deletions runner/codegen/genkit/model-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ModelReference } from 'genkit';
import { GenkitPlugin, GenkitPluginV2 } from 'genkit/plugin';
import { RateLimiter } from 'limiter';
import { PromptDataMessage } from '../llm-runner.js';
import { LlmResponseFile } from '../../shared-interfaces.js';

export interface RateLimitConfig {
requestPerMinute: RateLimiter;
Expand Down Expand Up @@ -46,6 +47,12 @@ export abstract class GenkitModelProvider {
return key ? this.pluginFactory(key) : null;
}

/**
* Checks whether files generated by the LLM are valid.
* If not, the Genkit runner will throw an error.
*/
abstract validateGeneratedFiles(files: LlmResponseFile[]): boolean;

protected abstract pluginFactory(
apiKey: string
): GenkitPlugin | GenkitPluginV2;
Expand Down
4 changes: 4 additions & 0 deletions runner/codegen/genkit/providers/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export class ClaudeModelProvider extends GenkitModelProvider {
return {};
}

validateGeneratedFiles(): boolean {
return true;
}

private anthropicApi = lazy(() => {
return new Anthropic({ apiKey: this.getApiKey() || undefined });
});
Expand Down
10 changes: 10 additions & 0 deletions runner/codegen/genkit/providers/gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { lazy } from '../../../utils/lazy-creation.js';
import { GoogleGenAI, Part } from '@google/genai';
import { RateLimiter } from 'limiter';
import { LlmResponseFile } from '../../../shared-interfaces.js';

export class GeminiModelProvider extends GenkitModelProvider {
readonly apiKeyVariableName = 'GEMINI_API_KEY';
Expand Down Expand Up @@ -74,6 +75,15 @@ export class GeminiModelProvider extends GenkitModelProvider {
return { thinkingConfig: { includeThoughts: opts.includeThoughts } };
}

validateGeneratedFiles(files: LlmResponseFile[]): boolean {
// Gemini responses occasionally get truncated on `class=`.
// Consider these cases as invalid so they don't influence the results.
return (
files.length === 0 ||
!files.some((file) => file.code.trim().endsWith('class='))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to treat as "invalid", but two questions:

  • Wouldn't such output result in a failed build etc anyway?
  • This seems Angular specific? how can other frameworks configure this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If there's an API error, we ignore the full run further down so these won't show up at all.
  2. It's basically a no-op for other frameworks so I figured that we don't really need to configure it.

Copy link
Member

@devversion devversion Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, for (2) I was thinking other frameworks might see same issues and want to "error out gracefully" then. We can always follow up on this

);
}

private async countGeminiTokens(
prompt: PromptDataForCounting,
modelName: string
Expand Down
4 changes: 4 additions & 0 deletions runner/codegen/genkit/providers/grok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ export class GrokModelProvider extends GenkitModelProvider {
return {};
}

validateGeneratedFiles(): boolean {
return true;
}

private genkitPromptToXaiFormat(
prompt: PromptDataForCounting
): Array<{ role: string; content: string }> {
Expand Down
4 changes: 4 additions & 0 deletions runner/codegen/genkit/providers/open-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export class OpenAiModelProvider extends GenkitModelProvider {
return {};
}

validateGeneratedFiles(): boolean {
return true;
}

private genkitPromptToOpenAi(
prompt: PromptDataForCounting
): Array<{ role: string; content: string }> {
Expand Down
Loading