Skip to content

Commit 4786e9e

Browse files
attempt 1
1 parent ca9e0a1 commit 4786e9e

7 files changed

Lines changed: 60 additions & 23 deletions

File tree

src/api/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ export interface SingleCompletionHandler {
2828
}
2929

3030
export interface ApiHandler {
31-
createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream
32-
getModel(): { id: string; info: ModelInfo }
31+
// completePrompt(prompt: string): Promise<string>
32+
createMessage(systemPrompt: string, messages: any[]): AsyncGenerator<any>
33+
getModel(): { id: string; info: ModelInfo } | Promise<{ id: string; info: ModelInfo }>
3334

3435
/**
3536
* Counts tokens for content blocks
@@ -42,7 +43,7 @@ export interface ApiHandler {
4243
countTokens(content: Array<Anthropic.Messages.ContentBlockParam>): Promise<number>
4344
}
4445

45-
export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
46+
export function buildApiHandler(configuration: ApiConfiguration): ApiHandler | Promise<ApiHandler> {
4647
const { apiProvider, ...options } = configuration
4748
switch (apiProvider) {
4849
case "anthropic":
@@ -76,7 +77,7 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
7677
case "requesty":
7778
return new RequestyHandler(options)
7879
case "pearai":
79-
return new PearAiHandler(options)
80+
return PearAiHandler.create(options)
8081
case "human-relay":
8182
return new HumanRelayHandler(options)
8283
case "fake-ai":

src/api/providers/base-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export abstract class BaseProvider implements ApiHandler {
1515
// Cache the Tiktoken encoder instance since it's stateless
1616
private encoder: Tiktoken | null = null
1717
abstract createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream
18-
abstract getModel(): { id: string; info: ModelInfo }
18+
abstract getModel(): { id: string; info: ModelInfo } | Promise<{ id: string; info: ModelInfo }>
1919

2020
/**
2121
* Default token counting implementation using tiktoken

src/api/providers/pearai/pearai.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl
2525
private handler!: AnthropicHandler | PearAIGenericHandler
2626
private pearAiModelsResponse: PearAiModelsResponse | null = null
2727
private options: ApiHandlerOptions
28+
private initializationPromise: Promise<void> | null = null
2829

29-
constructor(options: ApiHandlerOptions) {
30+
// Private constructor - use the static create method instead
31+
private constructor(options: ApiHandlerOptions) {
3032
super()
3133
if (!options.pearaiApiKey) {
3234
vscode.window.showErrorMessage("PearAI API key not found.", "Login to PearAI").then(async (selection) => {
@@ -45,18 +47,16 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl
4547
vscode.commands.executeCommand("pearai.checkPearAITokens", undefined)
4648
}
4749
this.options = options
50+
// Start initialization immediately
51+
this.initializationPromise = this.initializeHandler(options)
52+
}
4853

49-
this.handler = new PearAIGenericHandler({
50-
...options,
51-
openAiBaseUrl: PEARAI_URL,
52-
openAiApiKey: options.pearaiApiKey,
53-
openAiModelId: "deepseek/deepseek-chat",
54-
})
55-
56-
// Then try to initialize the correct handler asynchronously
57-
this.initializeHandler(options).catch((error) => {
58-
console.error("Failed to initialize PearAI handler:", error)
59-
})
54+
// Static factory method that properly awaits initialization
55+
public static async create(options: ApiHandlerOptions): Promise<PearAiHandler> {
56+
const instance = new PearAiHandler(options)
57+
// Wait for initialization to complete
58+
await instance.initializationPromise
59+
return instance
6060
}
6161

6262
private async initializeHandler(options: ApiHandlerOptions): Promise<void> {
@@ -116,7 +116,15 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl
116116
}
117117
}
118118

119-
getModel(): { id: string; info: ModelInfo } {
119+
private async ensureInitialized(): Promise<void> {
120+
if (this.initializationPromise) {
121+
await this.initializationPromise
122+
}
123+
}
124+
125+
async getModel(): Promise<{ id: string; info: ModelInfo }> {
126+
await this.ensureInitialized()
127+
120128
if (this.options.apiModelId) {
121129
let modelInfo = null
122130
if (this.options.apiModelId.startsWith("pearai")) {
@@ -142,6 +150,7 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl
142150
}
143151

144152
async *createMessage(systemPrompt: string, messages: any[]): AsyncGenerator<any> {
153+
await this.ensureInitialized()
145154
const generator = this.handler.createMessage(systemPrompt, messages)
146155
let warningMsg = ""
147156

@@ -168,6 +177,7 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl
168177
}
169178

170179
async completePrompt(prompt: string): Promise<string> {
180+
await this.ensureInitialized()
171181
return this.handler.completePrompt(prompt)
172182
}
173183
}

src/core/Cline.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import { parseXml } from "../utils/xml"
8383
import { readLines } from "../integrations/misc/read-lines"
8484
import { getWorkspacePath } from "../utils/path"
8585
import { isBinaryFile } from "isbinaryfile"
86+
import { AnthropicHandler } from "../api/providers/anthropic"
8687

8788
type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
8889
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
@@ -208,7 +209,15 @@ export class Cline extends EventEmitter<ClineEvents> {
208209
this.instanceId = crypto.randomUUID().slice(0, 8)
209210
this.taskNumber = -1
210211
this.apiConfiguration = apiConfiguration
211-
this.api = buildApiHandler(apiConfiguration)
212+
213+
// Initialize with a temporary handler that will be replaced
214+
this.api = new AnthropicHandler(apiConfiguration)
215+
216+
// Then asynchronously initialize the correct handler
217+
this.initializeApiHandler(apiConfiguration).catch(error => {
218+
console.error("Failed to initialize API handler:", error)
219+
})
220+
212221
this.urlContentFetcher = new UrlContentFetcher(provider.context)
213222
this.browserSession = new BrowserSession(provider.context)
214223
this.customInstructions = customInstructions
@@ -4173,6 +4182,15 @@ export class Cline extends EventEmitter<ClineEvents> {
41734182
this.enableCheckpoints = false
41744183
}
41754184
}
4185+
4186+
private async initializeApiHandler(apiConfiguration: ApiConfiguration): Promise<void> {
4187+
const apiHandler = buildApiHandler(apiConfiguration)
4188+
if (apiHandler instanceof Promise) {
4189+
this.api = await apiHandler
4190+
} else {
4191+
this.api = apiHandler
4192+
}
4193+
}
41764194
}
41774195

41784196
function escapeRegExp(string: string): string {

src/core/webview/ClineProvider.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2159,7 +2159,12 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
21592159
await this.contextProxy.setApiConfiguration(apiConfiguration)
21602160

21612161
if (this.getCurrentCline()) {
2162-
this.getCurrentCline()!.api = buildApiHandler(apiConfiguration)
2162+
const apiHandler = buildApiHandler(apiConfiguration)
2163+
if (apiHandler instanceof Promise) {
2164+
this.getCurrentCline()!.api = await apiHandler
2165+
} else {
2166+
this.getCurrentCline()!.api = apiHandler
2167+
}
21632168
}
21642169
}
21652170

src/utils/__tests__/enhance-prompt.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ describe("enhancePrompt", () => {
3737
const result = await singleCompletionHandler(mockApiConfig, "Test prompt")
3838

3939
expect(result).toBe("Enhanced prompt")
40-
const handler = buildApiHandler(mockApiConfig)
40+
const handlerPromise = buildApiHandler(mockApiConfig)
41+
const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise))
4142
expect((handler as any).completePrompt).toHaveBeenCalledWith(`Test prompt`)
4243
})
4344

@@ -59,7 +60,8 @@ describe("enhancePrompt", () => {
5960
)
6061

6162
expect(result).toBe("Enhanced prompt")
62-
const handler = buildApiHandler(mockApiConfig)
63+
const handlerPromise = buildApiHandler(mockApiConfig)
64+
const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise))
6365
expect((handler as any).completePrompt).toHaveBeenCalledWith(`${customEnhancePrompt}\n\nTest prompt`)
6466
})
6567

src/utils/single-completion-handler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export async function singleCompletionHandler(apiConfiguration: ApiConfiguration
1313
throw new Error("No valid API configuration provided")
1414
}
1515

16-
const handler = buildApiHandler(apiConfiguration)
16+
const handlerPromise = buildApiHandler(apiConfiguration)
17+
const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise))
1718

1819
// Check if handler supports single completions
1920
if (!("completePrompt" in handler)) {

0 commit comments

Comments
 (0)