Skip to content

Commit 10369eb

Browse files
add pearai provider
1 parent f07109b commit 10369eb

File tree

6 files changed

+114
-2
lines changed

6 files changed

+114
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ roo-cline-*.vsix
1515
# Test environment
1616
.test_env
1717
.vscode-test/
18+
.aider*
19+
.env

src/api/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DeepSeekHandler } from "./providers/deepseek"
1414
import { MistralHandler } from "./providers/mistral"
1515
import { VsCodeLmHandler } from "./providers/vscode-lm"
1616
import { ApiStream } from "./transform/stream"
17+
import { PearAiHandler } from "./providers/pearai"
1718

1819
export interface SingleCompletionHandler {
1920
completePrompt(prompt: string): Promise<string>
@@ -53,6 +54,8 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
5354
return new VsCodeLmHandler(options)
5455
case "mistral":
5556
return new MistralHandler(options)
57+
case "pearai":
58+
return new PearAiHandler(options)
5659
default:
5760
return new AnthropicHandler(options)
5861
}

src/api/providers/pearai.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { OpenAiHandler } from "./openai"
2+
import { ApiHandlerOptions } from "../../shared/api"
3+
4+
export class PearAiHandler extends OpenAiHandler {
5+
constructor(options: ApiHandlerOptions) {
6+
if (!options.pearaiApiKey) {
7+
throw new Error("PearAI API key is required. Please provide it in the settings.")
8+
}
9+
super({
10+
...options,
11+
// Map PearAI specific options to OpenAI options for compatibility
12+
openAiApiKey: options.pearaiApiKey,
13+
openAiBaseUrl:
14+
options.pearaiBaseUrl ??
15+
"https://stingray-app-gb2an.ondigitalocean.app/pearai-server-api2/integrations/cline",
16+
openAiStreamingEnabled: true,
17+
})
18+
}
19+
}

src/core/webview/ClineProvider.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type SecretKey =
6262
| "openAiNativeApiKey"
6363
| "deepSeekApiKey"
6464
| "mistralApiKey"
65+
| "pearaiApiKey"
6566
type GlobalStateKey =
6667
| "apiProvider"
6768
| "apiModelId"
@@ -96,6 +97,10 @@ type GlobalStateKey =
9697
| "openRouterModelId"
9798
| "openRouterModelInfo"
9899
| "openRouterBaseUrl"
100+
| "pearaiModelId"
101+
| "pearaiModelInfo"
102+
| "pearaiBaseUrl"
103+
| "pearaiApiKey"
99104
| "openRouterUseMiddleOutTransform"
100105
| "allowedCommands"
101106
| "soundEnabled"
@@ -1347,6 +1352,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
13471352
openRouterUseMiddleOutTransform,
13481353
vsCodeLmModelSelector,
13491354
mistralApiKey,
1355+
pearaiApiKey,
1356+
pearaiBaseUrl,
1357+
pearaiModelId,
1358+
pearaiModelInfo,
13501359
} = apiConfiguration
13511360
await this.updateGlobalState("apiProvider", apiProvider)
13521361
await this.updateGlobalState("apiModelId", apiModelId)
@@ -1385,6 +1394,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
13851394
await this.updateGlobalState("openRouterUseMiddleOutTransform", openRouterUseMiddleOutTransform)
13861395
await this.updateGlobalState("vsCodeLmModelSelector", vsCodeLmModelSelector)
13871396
await this.storeSecret("mistralApiKey", mistralApiKey)
1397+
await this.updateGlobalState("pearaiModelId", pearaiModelId)
1398+
await this.updateGlobalState("pearaiModelInfo", pearaiModelInfo)
1399+
await this.storeSecret("pearaiApiKey", pearaiApiKey)
1400+
await this.updateGlobalState("pearaiBaseUrl", pearaiBaseUrl)
13881401
if (this.cline) {
13891402
this.cline.api = buildApiHandler(apiConfiguration)
13901403
}
@@ -2003,6 +2016,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
20032016
openAiNativeApiKey,
20042017
deepSeekApiKey,
20052018
mistralApiKey,
2019+
pearaiApiKey,
2020+
pearaiBaseUrl,
2021+
pearaiModelId,
2022+
pearaiModelInfo,
20062023
azureApiVersion,
20072024
openAiStreamingEnabled,
20082025
openRouterModelId,
@@ -2073,6 +2090,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
20732090
this.getSecret("openAiNativeApiKey") as Promise<string | undefined>,
20742091
this.getSecret("deepSeekApiKey") as Promise<string | undefined>,
20752092
this.getSecret("mistralApiKey") as Promise<string | undefined>,
2093+
this.getSecret("pearaiApiKey") as Promise<string | undefined>,
2094+
this.getGlobalState("pearaiBaseUrl") as Promise<string | undefined>,
2095+
this.getGlobalState("pearaiModelId") as Promise<string | undefined>,
2096+
this.getGlobalState("pearaiModelInfo") as Promise<ModelInfo | undefined>,
20762097
this.getGlobalState("azureApiVersion") as Promise<string | undefined>,
20772098
this.getGlobalState("openAiStreamingEnabled") as Promise<boolean | undefined>,
20782099
this.getGlobalState("openRouterModelId") as Promise<string | undefined>,
@@ -2160,6 +2181,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
21602181
openAiNativeApiKey,
21612182
deepSeekApiKey,
21622183
mistralApiKey,
2184+
pearaiApiKey,
2185+
pearaiBaseUrl,
2186+
pearaiModelId,
2187+
pearaiModelInfo,
21632188
azureApiVersion,
21642189
openAiStreamingEnabled,
21652190
openRouterModelId,

src/shared/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type ApiProvider =
1414
| "deepseek"
1515
| "vscode-lm"
1616
| "mistral"
17+
| "pearai"
1718

1819
export interface ApiHandlerOptions {
1920
apiModelId?: string
@@ -57,6 +58,10 @@ export interface ApiHandlerOptions {
5758
deepSeekBaseUrl?: string
5859
deepSeekApiKey?: string
5960
includeMaxTokens?: boolean
61+
pearaiApiKey?: string
62+
pearaiBaseUrl?: string
63+
pearaiModelId?: string
64+
pearaiModelInfo?: ModelInfo
6065
}
6166

6267
export type ApiConfiguration = ApiHandlerOptions & {

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Checkbox, Dropdown, Pane } from "vscrui"
22
import type { DropdownOption } from "vscrui"
3-
import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
3+
import {
4+
VSCodeButton,
5+
VSCodeLink,
6+
VSCodeRadio,
7+
VSCodeRadioGroup,
8+
VSCodeTextField,
9+
} from "@vscode/webview-ui-toolkit/react"
410
import { Fragment, memo, useCallback, useEffect, useMemo, useState } from "react"
511
import { useEvent, useInterval } from "react-use"
612
import {
@@ -39,6 +45,8 @@ import OpenRouterModelPicker, {
3945
import OpenAiModelPicker from "./OpenAiModelPicker"
4046
import GlamaModelPicker from "./GlamaModelPicker"
4147

48+
const PEARAI_DEFAULT_URL = "https://stingray-app-gb2an.ondigitalocean.app/pearai-server-api2/integrations/cline"
49+
4250
interface ApiOptionsProps {
4351
apiErrorMessage?: string
4452
modelIdErrorMessage?: string
@@ -52,6 +60,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
5260
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
5361
const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion)
5462
const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl)
63+
const [pearaiBaseUrlSelected, setPearaiBaseUrlSelected] = useState(!!apiConfiguration?.pearaiBaseUrl)
5564
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
5665

5766
const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => {
@@ -134,6 +143,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
134143
}}
135144
style={{ minWidth: 130, position: "relative", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 1 }}
136145
options={[
146+
{ value: "pearai", label: "PearAI" },
137147
{ value: "openrouter", label: "OpenRouter" },
138148
{ value: "anthropic", label: "Anthropic" },
139149
{ value: "gemini", label: "Google Gemini" },
@@ -151,6 +161,47 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
151161
/>
152162
</div>
153163

164+
{selectedProvider === "pearai" && (
165+
<div>
166+
<VSCodeTextField
167+
value={apiConfiguration?.pearaiApiKey || ""}
168+
style={{ width: "100%" }}
169+
type="password"
170+
onInput={handleInputChange("pearaiApiKey")}
171+
placeholder="Enter API Key...">
172+
<span style={{ fontWeight: 500 }}>PearAI API Key</span>
173+
</VSCodeTextField>
174+
<VSCodeTextField
175+
value={apiConfiguration?.pearaiBaseUrl || PEARAI_DEFAULT_URL}
176+
style={{ width: "100%" }}
177+
type="url"
178+
onInput={handleInputChange("pearaiBaseUrl")}
179+
placeholder={PEARAI_DEFAULT_URL}>
180+
<span style={{ fontWeight: 500 }}>Base URL</span>
181+
</VSCodeTextField>
182+
{apiConfiguration?.pearaiBaseUrl && apiConfiguration.pearaiBaseUrl !== PEARAI_DEFAULT_URL && (
183+
<VSCodeButton
184+
onClick={() => {
185+
handleInputChange("pearaiBaseUrl")({
186+
target: {
187+
value: PEARAI_DEFAULT_URL,
188+
},
189+
})
190+
}}>
191+
Reset to default URL
192+
</VSCodeButton>
193+
)}
194+
<p
195+
style={{
196+
fontSize: "12px",
197+
marginTop: "5px",
198+
color: "var(--vscode-descriptionForeground)",
199+
}}>
200+
This key is stored locally and only used to make API requests from this extension.
201+
</p>
202+
</div>
203+
)}
204+
154205
{selectedProvider === "anthropic" && (
155206
<div>
156207
<VSCodeTextField
@@ -1302,7 +1353,8 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
13021353
selectedProvider !== "openrouter" &&
13031354
selectedProvider !== "openai" &&
13041355
selectedProvider !== "ollama" &&
1305-
selectedProvider !== "lmstudio" && (
1356+
selectedProvider !== "lmstudio" &&
1357+
selectedProvider !== "pearai" && (
13061358
<>
13071359
<div className="dropdown-container">
13081360
<label htmlFor="model-id">
@@ -1552,6 +1604,12 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
15521604
supportsImages: false, // VSCode LM API currently doesn't support images
15531605
},
15541606
}
1607+
case "pearai":
1608+
return {
1609+
selectedProvider: provider,
1610+
selectedModelId: apiConfiguration?.pearaiModelId || "",
1611+
selectedModelInfo: apiConfiguration?.pearaiModelInfo || openAiModelInfoSaneDefaults,
1612+
}
15551613
default:
15561614
return getProviderData(anthropicModels, anthropicDefaultModelId)
15571615
}

0 commit comments

Comments
 (0)