Skip to content

fix(title): fall back to configured provider for title generation - #263

Open
philippecottier wants to merge 1 commit into
Adam-CAD:masterfrom
philippecottier:fix/title-generator-provider-fallback
Open

philippecottier wants to merge 1 commit into
Adam-CAD:masterfrom
philippecottier:fix/title-generator-provider-fallback

Conversation

@philippecottier

@philippecottier philippecottier commented Sep 16, 2026

Copy link
Copy Markdown

Problem

The title generator (src/routes/api/title-generator.ts) always called Anthropic, so a self-hosted instance configured with only an OpenAI/OpenRouter or Google key had every conversation stuck on "New Conversation" — the request failed silently.

Fix

Keep Anthropic as the default (no change for the hosted service), then fall back to OpenRouter then Google based on the configured API key, returning null (→ "New Conversation") if none is set. Provider selection reads process.env; no new dependencies.

Notes

  • Google model kept in sync with the current default (gemini-3.8-flash).
  • tsc -b, eslint, and prettier all pass.

Summary by cubic

Title generation now uses the configured provider instead of always calling Anthropic, so self-hosted instances with only OpenRouter or Google credentials can generate conversation titles.

  • Keeps Anthropic as the default, then falls back to OpenRouter and Google based on configured API keys.
  • Returns "New Conversation" when no key is configured or generation fails.
  • Adds no dependencies.

Written for commit 7f33da6. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Sep 16, 2026

Copy link
Copy Markdown

@philippecottier is attempting to deploy a commit to the Adam Team on Vercel.

A member of the Team first needs to authorize it.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/routes/api/title-generator.ts">

<violation number="1" location="src/routes/api/title-generator.ts:48">
P2: When a self-host is configured with only `OPENAI_API_KEY`, this selector skips it and returns `New Conversation`. Add a direct OpenAI branch or include that key in the documented provider selection.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/routes/api/title-generator.ts Outdated
});
}

if (process.env.OPENROUTER_API_KEY) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When a self-host is configured with only OPENAI_API_KEY, this selector skips it and returns New Conversation. Add a direct OpenAI branch or include that key in the documented provider selection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/routes/api/title-generator.ts, line 48:

<comment>When a self-host is configured with only `OPENAI_API_KEY`, this selector skips it and returns `New Conversation`. Add a direct OpenAI branch or include that key in the documented provider selection.</comment>

<file context>
@@ -25,6 +26,78 @@ function textFromParts(parts: unknown): string {
+    });
+  }
+
+  if (process.env.OPENROUTER_API_KEY) {
+    const response = await fetch(
+      'https://openrouter.ai/api/v1/chat/completions',
</file context>

@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because installations configured only with OPENAI_API_KEY still cannot generate titles.

Findings

  1. P1 Direct OpenAI Remains Unsupported
  2. P2 Provider Fallbacks Lack Tests

Summary

The PR adds configured-provider fallback for conversation-title generation.

  • Keeps Anthropic as the first-choice provider.
  • Uses OpenRouter and then Google when their respective keys are configured.
  • Safely validates provider response structures without type assertions.
  • Returns “New Conversation” when input, configuration, requests, or responses cannot produce a title.
  • The unresolved direct-OpenAI support and provider-test findings remain in their existing review threads.

Reviews (2) · Last reviewed commit: "fix(title): fall back to configured prov..."

Comment thread src/routes/api/title-generator.ts Outdated
Comment on lines +32 to +36
* Anthropic stays the default (unchanged for the hosted app), but self-hosters
* who only have an OpenAI/OpenRouter or Google key would otherwise get every
* conversation stuck on "New Conversation", since the previous implementation
* always called Anthropic. Fall back to those providers when no Anthropic key
* is set. Returns null when no provider is available.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Direct OpenAI Remains Unsupported

For an installation configured only with the documented OPENAI_API_KEY, this function checks neither that key nor a direct OpenAI provider. It falls through to null, so conversations remain stuck on “New Conversation” despite the comment claiming support for “OpenAI/OpenRouter.” Add direct OpenAI handling or correct the claimed supported configuration.

Comment thread src/routes/api/title-generator.ts Outdated
Comment thread src/routes/api/title-generator.ts Outdated
Comment on lines +38 to +99
async function generateTitleText(text: string): Promise<string | null> {
if (process.env.ANTHROPIC_API_KEY) {
return createAnthropicText({
model: 'claude-haiku-4-5-20251001',
maxTokens: 100,
system: TITLE_SYSTEM_PROMPT,
content: text,
});
}

if (process.env.OPENROUTER_API_KEY) {
const response = await fetch(
'https://openrouter.ai/api/v1/chat/completions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
},
body: JSON.stringify({
model: 'openai/gpt-5.6-sol',
max_tokens: 100,
messages: [
{ role: 'system', content: TITLE_SYSTEM_PROMPT },
{ role: 'user', content: text },
],
}),
},
);
if (!response.ok) {
throw new Error(`openrouter ${response.status}`);
}
const data = (await response.json()) as {
choices?: Array<{ message?: { content?: string } }>;
};
return data.choices?.[0]?.message?.content ?? null;
}

if (process.env.GOOGLE_API_KEY) {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent?key=${process.env.GOOGLE_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
systemInstruction: { parts: [{ text: TITLE_SYSTEM_PROMPT }] },
contents: [{ role: 'user', parts: [{ text }] }],
generationConfig: { maxOutputTokens: 100 },
}),
},
);
if (!response.ok) {
throw new Error(`google ${response.status}`);
}
const data = (await response.json()) as {
candidates?: Array<{ content?: { parts?: Array<{ text?: string }> } }>;
};
return data.candidates?.[0]?.content?.parts?.[0]?.text ?? null;
}

return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Provider Fallbacks Lack Tests

The new provider selection, request, error, and response-parsing branches have no tests covering each key configuration and returned title. Because failures are deliberately converted into “New Conversation,” a broken request shape or routing regression would look exactly like the original bug. Add mocked tests that assert the actual title for Anthropic, OpenRouter, Google, and no-provider configurations.

The title generator always called Anthropic, so self-hosted instances configured with only an OpenAI/OpenRouter or Google key had every conversation stuck on "New Conversation". Keep Anthropic as the default, then fall back to OpenRouter and Google based on the configured API key.
@philippecottier
philippecottier force-pushed the fix/title-generator-provider-fallback branch from 2bd797c to 7f33da6 Compare September 16, 2026 16:46
@philippecottier

Copy link
Copy Markdown
Author

Thanks — addressed the review. Google's API key is now sent in the x-goog-api-key header instead of the URL; the as casts on provider responses are removed (parsed from unknown with isRecord guards); and the long line is split, so nothing exceeds 100 chars (tsc/eslint/prettier all pass).

On direct OpenAI: the chat has no direct-OpenAI provider (ChatProvider is 'anthropic' | 'google' | 'openrouter') — OpenAI runs through OpenRouter (openai/gpt-5.6-sol). This fallback now mirrors that exact set (Anthropic, then OpenRouter, then Google), and I fixed the misleading "OpenAI" wording. Happy to add a dedicated OPENAI_API_KEY branch if you'd prefer the title route to diverge from the chat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant