mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Adds unbound provider to roo cline
This commit is contained in:
committed by
Vignesh Subbiah
parent
db0ec64d1c
commit
62dcfbe549
@@ -14,6 +14,7 @@ import { DeepSeekHandler } from "./providers/deepseek"
|
|||||||
import { MistralHandler } from "./providers/mistral"
|
import { MistralHandler } from "./providers/mistral"
|
||||||
import { VsCodeLmHandler } from "./providers/vscode-lm"
|
import { VsCodeLmHandler } from "./providers/vscode-lm"
|
||||||
import { ApiStream } from "./transform/stream"
|
import { ApiStream } from "./transform/stream"
|
||||||
|
import { UnboundHandler } from "./providers/unbound"
|
||||||
|
|
||||||
export interface SingleCompletionHandler {
|
export interface SingleCompletionHandler {
|
||||||
completePrompt(prompt: string): Promise<string>
|
completePrompt(prompt: string): Promise<string>
|
||||||
@@ -53,6 +54,8 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
|
|||||||
return new VsCodeLmHandler(options)
|
return new VsCodeLmHandler(options)
|
||||||
case "mistral":
|
case "mistral":
|
||||||
return new MistralHandler(options)
|
return new MistralHandler(options)
|
||||||
|
case "unbound":
|
||||||
|
return new UnboundHandler(options)
|
||||||
default:
|
default:
|
||||||
return new AnthropicHandler(options)
|
return new AnthropicHandler(options)
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/api/providers/unbound.ts
Normal file
59
src/api/providers/unbound.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { ApiHandlerOptions, unboundModels, UnboundModelId, unboundDefaultModelId, ModelInfo } from "../../shared/api"
|
||||||
|
import { ApiStream } from "../transform/stream"
|
||||||
|
import { Anthropic } from "@anthropic-ai/sdk"
|
||||||
|
import { ApiHandler } from "../index"
|
||||||
|
|
||||||
|
export class UnboundHandler implements ApiHandler {
|
||||||
|
private unboundApiKey: string
|
||||||
|
private unboundModelId: string
|
||||||
|
private unboundBaseUrl: string = "https://ai-gateway-43843357113.us-west1.run.app/v1"
|
||||||
|
private options: ApiHandlerOptions
|
||||||
|
|
||||||
|
constructor(options: ApiHandlerOptions) {
|
||||||
|
this.options = options
|
||||||
|
this.unboundApiKey = options.unboundApiKey || ""
|
||||||
|
this.unboundModelId = options.unboundModelId || ""
|
||||||
|
}
|
||||||
|
|
||||||
|
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||||
|
const response = await fetch(`${this.unboundBaseUrl}/chat/completions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${this.unboundApiKey}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: this.unboundModelId,
|
||||||
|
messages: [{ role: "system", content: systemPrompt }, ...messages],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
yield {
|
||||||
|
type: "text",
|
||||||
|
text: data.choices[0]?.message?.content || "",
|
||||||
|
}
|
||||||
|
yield {
|
||||||
|
type: "usage",
|
||||||
|
inputTokens: data.usage?.prompt_tokens || 0,
|
||||||
|
outputTokens: data.usage?.completion_tokens || 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getModel(): { id: UnboundModelId; info: ModelInfo } {
|
||||||
|
const modelId = this.options.apiModelId
|
||||||
|
if (modelId && modelId in unboundModels) {
|
||||||
|
const id = modelId as UnboundModelId
|
||||||
|
return { id, info: unboundModels[id] }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: unboundDefaultModelId,
|
||||||
|
info: unboundModels[unboundDefaultModelId],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@ type SecretKey =
|
|||||||
| "openAiNativeApiKey"
|
| "openAiNativeApiKey"
|
||||||
| "deepSeekApiKey"
|
| "deepSeekApiKey"
|
||||||
| "mistralApiKey"
|
| "mistralApiKey"
|
||||||
|
| "unboundApiKey"
|
||||||
type GlobalStateKey =
|
type GlobalStateKey =
|
||||||
| "apiProvider"
|
| "apiProvider"
|
||||||
| "apiModelId"
|
| "apiModelId"
|
||||||
@@ -120,6 +121,7 @@ type GlobalStateKey =
|
|||||||
| "experimentalDiffStrategy"
|
| "experimentalDiffStrategy"
|
||||||
| "autoApprovalEnabled"
|
| "autoApprovalEnabled"
|
||||||
| "customModes" // Array of custom modes
|
| "customModes" // Array of custom modes
|
||||||
|
| "unboundModelId"
|
||||||
|
|
||||||
export const GlobalFileNames = {
|
export const GlobalFileNames = {
|
||||||
apiConversationHistory: "api_conversation_history.json",
|
apiConversationHistory: "api_conversation_history.json",
|
||||||
@@ -1309,6 +1311,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
openRouterUseMiddleOutTransform,
|
openRouterUseMiddleOutTransform,
|
||||||
vsCodeLmModelSelector,
|
vsCodeLmModelSelector,
|
||||||
mistralApiKey,
|
mistralApiKey,
|
||||||
|
unboundApiKey,
|
||||||
|
unboundModelId,
|
||||||
} = apiConfiguration
|
} = apiConfiguration
|
||||||
await this.updateGlobalState("apiProvider", apiProvider)
|
await this.updateGlobalState("apiProvider", apiProvider)
|
||||||
await this.updateGlobalState("apiModelId", apiModelId)
|
await this.updateGlobalState("apiModelId", apiModelId)
|
||||||
@@ -1347,6 +1351,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
await this.updateGlobalState("openRouterUseMiddleOutTransform", openRouterUseMiddleOutTransform)
|
await this.updateGlobalState("openRouterUseMiddleOutTransform", openRouterUseMiddleOutTransform)
|
||||||
await this.updateGlobalState("vsCodeLmModelSelector", vsCodeLmModelSelector)
|
await this.updateGlobalState("vsCodeLmModelSelector", vsCodeLmModelSelector)
|
||||||
await this.storeSecret("mistralApiKey", mistralApiKey)
|
await this.storeSecret("mistralApiKey", mistralApiKey)
|
||||||
|
await this.storeSecret("unboundApiKey", unboundApiKey)
|
||||||
|
await this.updateGlobalState("unboundModelId", unboundModelId)
|
||||||
if (this.cline) {
|
if (this.cline) {
|
||||||
this.cline.api = buildApiHandler(apiConfiguration)
|
this.cline.api = buildApiHandler(apiConfiguration)
|
||||||
}
|
}
|
||||||
@@ -2001,6 +2007,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
experimentalDiffStrategy,
|
experimentalDiffStrategy,
|
||||||
autoApprovalEnabled,
|
autoApprovalEnabled,
|
||||||
customModes,
|
customModes,
|
||||||
|
unboundApiKey,
|
||||||
|
unboundModelId,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||||
@@ -2070,6 +2078,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>,
|
this.getGlobalState("experimentalDiffStrategy") as Promise<boolean | undefined>,
|
||||||
this.getGlobalState("autoApprovalEnabled") as Promise<boolean | undefined>,
|
this.getGlobalState("autoApprovalEnabled") as Promise<boolean | undefined>,
|
||||||
this.customModesManager.getCustomModes(),
|
this.customModesManager.getCustomModes(),
|
||||||
|
this.getSecret("unboundApiKey") as Promise<string | undefined>,
|
||||||
|
this.getGlobalState("unboundModelId") as Promise<string | undefined>,
|
||||||
])
|
])
|
||||||
|
|
||||||
let apiProvider: ApiProvider
|
let apiProvider: ApiProvider
|
||||||
@@ -2125,6 +2135,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
openRouterBaseUrl,
|
openRouterBaseUrl,
|
||||||
openRouterUseMiddleOutTransform,
|
openRouterUseMiddleOutTransform,
|
||||||
vsCodeLmModelSelector,
|
vsCodeLmModelSelector,
|
||||||
|
unboundApiKey,
|
||||||
|
unboundModelId,
|
||||||
},
|
},
|
||||||
lastShownAnnouncementId,
|
lastShownAnnouncementId,
|
||||||
customInstructions,
|
customInstructions,
|
||||||
@@ -2273,6 +2285,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
"openAiNativeApiKey",
|
"openAiNativeApiKey",
|
||||||
"deepSeekApiKey",
|
"deepSeekApiKey",
|
||||||
"mistralApiKey",
|
"mistralApiKey",
|
||||||
|
"unboundApiKey",
|
||||||
]
|
]
|
||||||
for (const key of secretKeys) {
|
for (const key of secretKeys) {
|
||||||
await this.storeSecret(key, undefined)
|
await this.storeSecret(key, undefined)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type ApiProvider =
|
|||||||
| "deepseek"
|
| "deepseek"
|
||||||
| "vscode-lm"
|
| "vscode-lm"
|
||||||
| "mistral"
|
| "mistral"
|
||||||
|
| "unbound"
|
||||||
|
|
||||||
export interface ApiHandlerOptions {
|
export interface ApiHandlerOptions {
|
||||||
apiModelId?: string
|
apiModelId?: string
|
||||||
@@ -57,6 +58,8 @@ export interface ApiHandlerOptions {
|
|||||||
deepSeekBaseUrl?: string
|
deepSeekBaseUrl?: string
|
||||||
deepSeekApiKey?: string
|
deepSeekApiKey?: string
|
||||||
includeMaxTokens?: boolean
|
includeMaxTokens?: boolean
|
||||||
|
unboundApiKey?: string
|
||||||
|
unboundModelId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiConfiguration = ApiHandlerOptions & {
|
export type ApiConfiguration = ApiHandlerOptions & {
|
||||||
@@ -593,3 +596,11 @@ export const mistralModels = {
|
|||||||
outputPrice: 0.9,
|
outputPrice: 0.9,
|
||||||
},
|
},
|
||||||
} as const satisfies Record<string, ModelInfo>
|
} as const satisfies Record<string, ModelInfo>
|
||||||
|
|
||||||
|
// Unbound Security
|
||||||
|
export type UnboundModelId = keyof typeof unboundModels
|
||||||
|
export const unboundDefaultModelId = "gpt-4o"
|
||||||
|
export const unboundModels = {
|
||||||
|
"gpt-4o": openAiNativeModels["gpt-4o"],
|
||||||
|
"claude-3-5-sonnet-20241022": anthropicModels["claude-3-5-sonnet-20241022"],
|
||||||
|
} as const satisfies Record<string, ModelInfo>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
openRouterDefaultModelInfo,
|
openRouterDefaultModelInfo,
|
||||||
vertexDefaultModelId,
|
vertexDefaultModelId,
|
||||||
vertexModels,
|
vertexModels,
|
||||||
|
unboundDefaultModelId,
|
||||||
|
unboundModels,
|
||||||
} from "../../../../src/shared/api"
|
} from "../../../../src/shared/api"
|
||||||
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
|
import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage"
|
||||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||||
@@ -147,6 +149,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
|
|||||||
{ value: "mistral", label: "Mistral" },
|
{ value: "mistral", label: "Mistral" },
|
||||||
{ value: "lmstudio", label: "LM Studio" },
|
{ value: "lmstudio", label: "LM Studio" },
|
||||||
{ value: "ollama", label: "Ollama" },
|
{ value: "ollama", label: "Ollama" },
|
||||||
|
{ value: "unbound", label: "Unbound" },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1283,6 +1286,27 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{selectedProvider === "unbound" && (
|
||||||
|
<div>
|
||||||
|
<VSCodeTextField
|
||||||
|
value={apiConfiguration?.unboundApiKey || ""}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
type="password"
|
||||||
|
onChange={handleInputChange("unboundApiKey")}
|
||||||
|
placeholder="Enter API Key...">
|
||||||
|
<span style={{ fontWeight: 500 }}>Unbound API Key</span>
|
||||||
|
</VSCodeTextField>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
fontSize: "12px",
|
||||||
|
marginTop: 3,
|
||||||
|
color: "var(--vscode-descriptionForeground)",
|
||||||
|
}}>
|
||||||
|
This key is stored locally and only used to make API requests from this extension.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{apiErrorMessage && (
|
{apiErrorMessage && (
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
@@ -1315,6 +1339,7 @@ const ApiOptions = ({ apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) =
|
|||||||
{selectedProvider === "openai-native" && createDropdown(openAiNativeModels)}
|
{selectedProvider === "openai-native" && createDropdown(openAiNativeModels)}
|
||||||
{selectedProvider === "deepseek" && createDropdown(deepSeekModels)}
|
{selectedProvider === "deepseek" && createDropdown(deepSeekModels)}
|
||||||
{selectedProvider === "mistral" && createDropdown(mistralModels)}
|
{selectedProvider === "mistral" && createDropdown(mistralModels)}
|
||||||
|
{selectedProvider === "unbound" && createDropdown(unboundModels)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ModelInfoView
|
<ModelInfoView
|
||||||
@@ -1552,6 +1577,13 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
|
|||||||
supportsImages: false, // VSCode LM API currently doesn't support images
|
supportsImages: false, // VSCode LM API currently doesn't support images
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
case "unbound":
|
||||||
|
return getProviderData(unboundModels, unboundDefaultModelId)
|
||||||
|
// return {
|
||||||
|
// selectedProvider: provider,
|
||||||
|
// selectedModelId: apiConfiguration?.unboundModelId || unboundDefaultModelId,
|
||||||
|
// selectedModelInfo: openAiModelInfoSaneDefaults,
|
||||||
|
// }
|
||||||
default:
|
default:
|
||||||
return getProviderData(anthropicModels, anthropicDefaultModelId)
|
return getProviderData(anthropicModels, anthropicDefaultModelId)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user