From 4786e9e194e51b1cafaa1d866d64c8170cfcb935 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Thu, 10 Apr 2025 22:36:17 +0530 Subject: [PATCH] attempt 1 --- src/api/index.ts | 9 +++--- src/api/providers/base-provider.ts | 2 +- src/api/providers/pearai/pearai.ts | 36 ++++++++++++++-------- src/core/Cline.ts | 20 +++++++++++- src/core/webview/ClineProvider.ts | 7 ++++- src/utils/__tests__/enhance-prompt.test.ts | 6 ++-- src/utils/single-completion-handler.ts | 3 +- 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index b5777439843..45f3d5ea5d2 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -28,8 +28,9 @@ export interface SingleCompletionHandler { } export interface ApiHandler { - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream - getModel(): { id: string; info: ModelInfo } + // completePrompt(prompt: string): Promise + createMessage(systemPrompt: string, messages: any[]): AsyncGenerator + getModel(): { id: string; info: ModelInfo } | Promise<{ id: string; info: ModelInfo }> /** * Counts tokens for content blocks @@ -42,7 +43,7 @@ export interface ApiHandler { countTokens(content: Array): Promise } -export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { +export function buildApiHandler(configuration: ApiConfiguration): ApiHandler | Promise { const { apiProvider, ...options } = configuration switch (apiProvider) { case "anthropic": @@ -76,7 +77,7 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { case "requesty": return new RequestyHandler(options) case "pearai": - return new PearAiHandler(options) + return PearAiHandler.create(options) case "human-relay": return new HumanRelayHandler(options) case "fake-ai": diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index 34156e4adfe..11154ac1692 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -15,7 +15,7 @@ export abstract class BaseProvider implements ApiHandler { // Cache the Tiktoken encoder instance since it's stateless private encoder: Tiktoken | null = null abstract createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream - abstract getModel(): { id: string; info: ModelInfo } + abstract getModel(): { id: string; info: ModelInfo } | Promise<{ id: string; info: ModelInfo }> /** * Default token counting implementation using tiktoken diff --git a/src/api/providers/pearai/pearai.ts b/src/api/providers/pearai/pearai.ts index 58e1e5e9126..eed144b4af0 100644 --- a/src/api/providers/pearai/pearai.ts +++ b/src/api/providers/pearai/pearai.ts @@ -25,8 +25,10 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl private handler!: AnthropicHandler | PearAIGenericHandler private pearAiModelsResponse: PearAiModelsResponse | null = null private options: ApiHandlerOptions + private initializationPromise: Promise | null = null - constructor(options: ApiHandlerOptions) { + // Private constructor - use the static create method instead + private constructor(options: ApiHandlerOptions) { super() if (!options.pearaiApiKey) { 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 vscode.commands.executeCommand("pearai.checkPearAITokens", undefined) } this.options = options + // Start initialization immediately + this.initializationPromise = this.initializeHandler(options) + } - this.handler = new PearAIGenericHandler({ - ...options, - openAiBaseUrl: PEARAI_URL, - openAiApiKey: options.pearaiApiKey, - openAiModelId: "deepseek/deepseek-chat", - }) - - // Then try to initialize the correct handler asynchronously - this.initializeHandler(options).catch((error) => { - console.error("Failed to initialize PearAI handler:", error) - }) + // Static factory method that properly awaits initialization + public static async create(options: ApiHandlerOptions): Promise { + const instance = new PearAiHandler(options) + // Wait for initialization to complete + await instance.initializationPromise + return instance } private async initializeHandler(options: ApiHandlerOptions): Promise { @@ -116,7 +116,15 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl } } - getModel(): { id: string; info: ModelInfo } { + private async ensureInitialized(): Promise { + if (this.initializationPromise) { + await this.initializationPromise + } + } + + async getModel(): Promise<{ id: string; info: ModelInfo }> { + await this.ensureInitialized() + if (this.options.apiModelId) { let modelInfo = null if (this.options.apiModelId.startsWith("pearai")) { @@ -142,6 +150,7 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl } async *createMessage(systemPrompt: string, messages: any[]): AsyncGenerator { + await this.ensureInitialized() const generator = this.handler.createMessage(systemPrompt, messages) let warningMsg = "" @@ -168,6 +177,7 @@ export class PearAiHandler extends BaseProvider implements SingleCompletionHandl } async completePrompt(prompt: string): Promise { + await this.ensureInitialized() return this.handler.completePrompt(prompt) } } diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 6cb26fbbc61..8f2351e7bd7 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -83,6 +83,7 @@ import { parseXml } from "../utils/xml" import { readLines } from "../integrations/misc/read-lines" import { getWorkspacePath } from "../utils/path" import { isBinaryFile } from "isbinaryfile" +import { AnthropicHandler } from "../api/providers/anthropic" type ToolResponse = string | Array type UserContent = Array @@ -208,7 +209,15 @@ export class Cline extends EventEmitter { this.instanceId = crypto.randomUUID().slice(0, 8) this.taskNumber = -1 this.apiConfiguration = apiConfiguration - this.api = buildApiHandler(apiConfiguration) + + // Initialize with a temporary handler that will be replaced + this.api = new AnthropicHandler(apiConfiguration) + + // Then asynchronously initialize the correct handler + this.initializeApiHandler(apiConfiguration).catch(error => { + console.error("Failed to initialize API handler:", error) + }) + this.urlContentFetcher = new UrlContentFetcher(provider.context) this.browserSession = new BrowserSession(provider.context) this.customInstructions = customInstructions @@ -4173,6 +4182,15 @@ export class Cline extends EventEmitter { this.enableCheckpoints = false } } + + private async initializeApiHandler(apiConfiguration: ApiConfiguration): Promise { + const apiHandler = buildApiHandler(apiConfiguration) + if (apiHandler instanceof Promise) { + this.api = await apiHandler + } else { + this.api = apiHandler + } + } } function escapeRegExp(string: string): string { diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5a53eab36c5..fa836b5baae 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2159,7 +2159,12 @@ export class ClineProvider extends EventEmitter implements await this.contextProxy.setApiConfiguration(apiConfiguration) if (this.getCurrentCline()) { - this.getCurrentCline()!.api = buildApiHandler(apiConfiguration) + const apiHandler = buildApiHandler(apiConfiguration) + if (apiHandler instanceof Promise) { + this.getCurrentCline()!.api = await apiHandler + } else { + this.getCurrentCline()!.api = apiHandler + } } } diff --git a/src/utils/__tests__/enhance-prompt.test.ts b/src/utils/__tests__/enhance-prompt.test.ts index d3cca04c38b..ddbfb8098f6 100644 --- a/src/utils/__tests__/enhance-prompt.test.ts +++ b/src/utils/__tests__/enhance-prompt.test.ts @@ -37,7 +37,8 @@ describe("enhancePrompt", () => { const result = await singleCompletionHandler(mockApiConfig, "Test prompt") expect(result).toBe("Enhanced prompt") - const handler = buildApiHandler(mockApiConfig) + const handlerPromise = buildApiHandler(mockApiConfig) + const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise)) expect((handler as any).completePrompt).toHaveBeenCalledWith(`Test prompt`) }) @@ -59,7 +60,8 @@ describe("enhancePrompt", () => { ) expect(result).toBe("Enhanced prompt") - const handler = buildApiHandler(mockApiConfig) + const handlerPromise = buildApiHandler(mockApiConfig) + const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise)) expect((handler as any).completePrompt).toHaveBeenCalledWith(`${customEnhancePrompt}\n\nTest prompt`) }) diff --git a/src/utils/single-completion-handler.ts b/src/utils/single-completion-handler.ts index 5e049d47267..b99139dc110 100644 --- a/src/utils/single-completion-handler.ts +++ b/src/utils/single-completion-handler.ts @@ -13,7 +13,8 @@ export async function singleCompletionHandler(apiConfiguration: ApiConfiguration throw new Error("No valid API configuration provided") } - const handler = buildApiHandler(apiConfiguration) + const handlerPromise = buildApiHandler(apiConfiguration) + const handler = await (handlerPromise instanceof Promise ? handlerPromise : Promise.resolve(handlerPromise)) // Check if handler supports single completions if (!("completePrompt" in handler)) {