Skip to content

Commit 2fee2ce

Browse files
committed
🤖 refactor: use AIService.createModel for title generation
- generateWorkspaceName now calls AIService.createModel with the exact model string - Removed provider-specific logic and fallbacks; errors propagate via SendMessageError - Updated ipcMain to pass aiService instead of config _Generated with _
1 parent 896fd72 commit 2fee2ce

File tree

3 files changed

+10
-103
lines changed

3 files changed

+10
-103
lines changed

src/node/services/aiService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export class AIService extends EventEmitter {
250250
* constructor, ensuring automatic parity with Vercel AI SDK - any configuration options
251251
* supported by the provider will work without modification.
252252
*/
253-
private async createModel(
253+
async createModel(
254254
modelString: string,
255255
muxProviderOptions?: MuxProviderOptions
256256
): Promise<Result<LanguageModel, SendMessageError>> {

src/node/services/ipcMain.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ export class IpcMain {
176176
{
177177
const isErrLike = (v: unknown): v is { type: string } =>
178178
typeof v === "object" && v !== null && "type" in v;
179-
const nameResult = await generateWorkspaceName(message, options.model, this.config);
179+
const nameResult = await generateWorkspaceName(message, options.model, this.aiService);
180180
if (!nameResult.success) {
181181
const err = nameResult.error;
182182
if (isErrLike(err)) {
@@ -877,7 +877,7 @@ export class IpcMain {
877877
const nameResult = await generateWorkspaceName(
878878
messageText,
879879
"anthropic:claude-sonnet-4-5", // Use reasonable default model
880-
this.config
880+
this.aiService
881881
);
882882
if (nameResult.success) {
883883
const branchName = nameResult.data;

src/node/services/workspaceTitleGenerator.ts

Lines changed: 7 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import { generateObject, type LanguageModel } from "ai";
1+
import { generateObject } from "ai";
22
import { z } from "zod";
3-
import type { Config } from "@/node/config";
3+
import type { AIService } from "./aiService";
44
import { log } from "./log";
5-
import { createAnthropic } from "@ai-sdk/anthropic";
6-
import { createOpenAI } from "@ai-sdk/openai";
7-
import { PROVIDER_REGISTRY } from "@/common/constants/providers";
8-
95
import type { Result } from "@/common/types/result";
106
import { Ok, Err } from "@/common/types/result";
117
import type { SendMessageError } from "@/common/types/errors";
@@ -27,39 +23,16 @@ const workspaceNameSchema = z.object({
2723
export async function generateWorkspaceName(
2824
message: string,
2925
modelString: string,
30-
config: Config
26+
aiService: AIService
3127
): Promise<Result<string, SendMessageError>> {
3228
try {
33-
const model = await getModelForTitleGeneration(modelString, config);
34-
35-
if (!model) {
36-
// Infer error from provider + config (mirrors createModel in aiService)
37-
const [providerName, modelId] = modelString.split(":", 2);
38-
if (!providerName || !modelId) {
39-
return Err({
40-
type: "invalid_model_string",
41-
message: `Invalid model string format: "${modelString}". Expected "provider:model-id"`,
42-
});
43-
}
44-
45-
const providers = config.loadProvidersConfig();
46-
47-
// Require API keys for providers that need them
48-
if (
49-
(providerName === "anthropic" ||
50-
providerName === "openai" ||
51-
providerName === "openrouter") &&
52-
!providers?.[providerName]?.apiKey
53-
) {
54-
return Err({ type: "api_key_not_found", provider: providerName });
55-
}
56-
57-
// Unknown/unsupported provider
58-
return Err({ type: "provider_not_supported", provider: providerName });
29+
const modelResult = await aiService.createModel(modelString);
30+
if (!modelResult.success) {
31+
return Err(modelResult.error);
5932
}
6033

6134
const result = await generateObject({
62-
model,
35+
model: modelResult.data,
6336
schema: workspaceNameSchema,
6437
prompt: `Generate a git-safe branch/workspace name for this development task:\n\n"${message}"\n\nRequirements:\n- Git-safe identifier (e.g., "automatic-title-generation")\n- Lowercase, hyphens only, no spaces\n- Concise (2-5 words) and descriptive of the task`,
6538
});
@@ -72,72 +45,6 @@ export async function generateWorkspaceName(
7245
}
7346
}
7447

75-
/**
76-
* Get model for title generation - prefers fast/cheap models
77-
* Priority: Haiku 4.5 (Anthropic) → GPT-5-mini (OpenAI) → fallback to user's model
78-
* Falls back to null if no provider configured
79-
*/
80-
async function getModelForTitleGeneration(
81-
modelString: string,
82-
config: Config
83-
): Promise<LanguageModel | null> {
84-
const providersConfig = config.loadProvidersConfig();
85-
86-
if (!providersConfig) {
87-
return null;
88-
}
89-
90-
try {
91-
// Use exactly what the user selected. Prefer their model over any defaults.
92-
const [providerName, modelId] = modelString.split(":", 2);
93-
if (!providerName || !modelId) {
94-
log.error("Invalid model string format:", modelString);
95-
return null;
96-
}
97-
98-
if (providerName === "anthropic") {
99-
if (!providersConfig.anthropic?.apiKey) return null;
100-
const provider = createAnthropic({ apiKey: String(providersConfig.anthropic.apiKey) });
101-
return provider(modelId);
102-
}
103-
104-
if (providerName === "openai") {
105-
if (!providersConfig.openai?.apiKey) return null;
106-
const provider = createOpenAI({ apiKey: String(providersConfig.openai.apiKey) });
107-
// Use Responses API model variant to match aiService
108-
return provider.responses(modelId);
109-
}
110-
111-
if (providerName === "openrouter") {
112-
if (!providersConfig.openrouter?.apiKey) return null;
113-
const { createOpenRouter } = await PROVIDER_REGISTRY.openrouter();
114-
const provider = createOpenRouter({
115-
apiKey: String(providersConfig.openrouter.apiKey),
116-
baseURL: providersConfig.openrouter.baseUrl,
117-
headers: providersConfig.openrouter.headers as Record<string, string> | undefined,
118-
});
119-
return provider(modelId);
120-
}
121-
122-
if (providerName === "ollama") {
123-
const { createOllama } = await PROVIDER_REGISTRY.ollama();
124-
const provider = createOllama({
125-
baseURL: (providersConfig.ollama?.baseUrl ?? providersConfig.ollama?.baseURL) as
126-
| string
127-
| undefined,
128-
});
129-
return provider(modelId);
130-
}
131-
132-
// Unknown provider
133-
log.error(`Provider ${providerName} not configured or not supported for titles`);
134-
return null;
135-
} catch (error) {
136-
log.error(`Failed to create model for title generation`, error);
137-
return null;
138-
}
139-
}
140-
14148
/**
14249
* Validate and sanitize branch name to be git-safe
14350
*/

0 commit comments

Comments
 (0)