mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Remove Glama provider integration and related references
- Deleted GlamaHandler export from providers index. - Removed Glama callback handling from ClineProvider. - Updated tests to exclude Glama API key and model checks. - Removed Glama-related configurations from ProfileValidator and validation functions. - Eliminated Glama components and settings from the UI. - Cleaned up localization files by removing Glama-specific entries. - Updated API utility functions to exclude Glama authentication.
This commit is contained in:
parent
af2e9ed725
commit
d5c92801ca
49 changed files with 17 additions and 745 deletions
|
|
@ -170,7 +170,6 @@ export function NewRun() {
|
|||
apiProvider,
|
||||
apiModelId,
|
||||
openRouterModelId,
|
||||
glamaModelId,
|
||||
requestyModelId,
|
||||
unboundModelId,
|
||||
ollamaModelId,
|
||||
|
|
@ -192,9 +191,6 @@ export function NewRun() {
|
|||
case "openrouter":
|
||||
setValue("model", openRouterModelId ?? "")
|
||||
break
|
||||
case "glama":
|
||||
setValue("model", glamaModelId ?? "")
|
||||
break
|
||||
case "requesty":
|
||||
setValue("model", requestyModelId ?? "")
|
||||
break
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { Equals, Keys, AssertEqual } from "./utils.js"
|
|||
|
||||
export const providerNames = [
|
||||
"anthropic",
|
||||
"glama",
|
||||
"openrouter",
|
||||
"bedrock",
|
||||
"vertex",
|
||||
|
|
@ -353,11 +352,6 @@ const anthropicSchema = z.object({
|
|||
anthropicUseAuthToken: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const glamaSchema = z.object({
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const openRouterSchema = z.object({
|
||||
openRouterApiKey: z.string().optional(),
|
||||
openRouterModelId: z.string().optional(),
|
||||
|
|
@ -488,11 +482,6 @@ export const providerSettingsSchemaDiscriminated = z
|
|||
apiProvider: z.literal("anthropic"),
|
||||
}),
|
||||
),
|
||||
glamaSchema.merge(
|
||||
z.object({
|
||||
apiProvider: z.literal("glama"),
|
||||
}),
|
||||
),
|
||||
openRouterSchema.merge(
|
||||
z.object({
|
||||
apiProvider: z.literal("openrouter"),
|
||||
|
|
@ -595,7 +584,6 @@ export const providerSettingsSchemaDiscriminated = z
|
|||
export const providerSettingsSchema = z.object({
|
||||
apiProvider: providerNamesSchema.optional(),
|
||||
...anthropicSchema.shape,
|
||||
...glamaSchema.shape,
|
||||
...openRouterSchema.shape,
|
||||
...bedrockSchema.shape,
|
||||
...vertexSchema.shape,
|
||||
|
|
@ -629,9 +617,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
|
|||
apiKey: undefined,
|
||||
anthropicBaseUrl: undefined,
|
||||
anthropicUseAuthToken: undefined,
|
||||
// Glama
|
||||
glamaModelId: undefined,
|
||||
glamaApiKey: undefined,
|
||||
// OpenRouter
|
||||
openRouterApiKey: undefined,
|
||||
openRouterModelId: undefined,
|
||||
|
|
@ -896,7 +881,6 @@ export const ROO_CODE_SETTINGS_KEYS = [...GLOBAL_SETTINGS_KEYS, ...PROVIDER_SETT
|
|||
export type SecretState = Pick<
|
||||
ProviderSettings,
|
||||
| "apiKey"
|
||||
| "glamaApiKey"
|
||||
| "openRouterApiKey"
|
||||
| "awsAccessKey"
|
||||
| "awsSecretKey"
|
||||
|
|
@ -915,7 +899,6 @@ type SecretStateRecord = Record<Keys<SecretState>, undefined>
|
|||
|
||||
const secretStateRecord: SecretStateRecord = {
|
||||
apiKey: undefined,
|
||||
glamaApiKey: undefined,
|
||||
openRouterApiKey: undefined,
|
||||
awsAccessKey: undefined,
|
||||
awsSecretKey: undefined,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export const globalSettingsSchema = z.object({
|
|||
allowedMaxRequests: z.number().nullish(),
|
||||
autoCondenseContext: z.boolean().optional(),
|
||||
autoCondenseContextPercent: z.number().optional(),
|
||||
maxConcurrentFileReads: z.number().optional(),
|
||||
maxConcurrentFileReads: z.number().optional(),
|
||||
|
||||
browserToolEnabled: z.boolean().optional(),
|
||||
browserViewportSize: z.string().optional(),
|
||||
|
|
@ -205,7 +205,6 @@ export type RooCodeSettings = GlobalSettings & ProviderSettings
|
|||
export type SecretState = Pick<
|
||||
ProviderSettings,
|
||||
| "apiKey"
|
||||
| "glamaApiKey"
|
||||
| "openRouterApiKey"
|
||||
| "awsAccessKey"
|
||||
| "awsSecretKey"
|
||||
|
|
@ -227,7 +226,6 @@ export type SecretState = Pick<
|
|||
|
||||
export const SECRET_STATE_KEYS = keysOf<SecretState>()([
|
||||
"apiKey",
|
||||
"glamaApiKey",
|
||||
"openRouterApiKey",
|
||||
"awsAccessKey",
|
||||
"awsSecretKey",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import { codebaseIndexProviderSchema } from "./codebase-index.js"
|
|||
|
||||
export const providerNames = [
|
||||
"anthropic",
|
||||
"glama",
|
||||
"openrouter",
|
||||
"bedrock",
|
||||
"vertex",
|
||||
|
|
@ -77,11 +76,6 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({
|
|||
anthropicUseAuthToken: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const glamaSchema = baseProviderSettingsSchema.extend({
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const openRouterSchema = baseProviderSettingsSchema.extend({
|
||||
openRouterApiKey: z.string().optional(),
|
||||
openRouterModelId: z.string().optional(),
|
||||
|
|
@ -208,7 +202,6 @@ const defaultSchema = z.object({
|
|||
|
||||
export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [
|
||||
anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })),
|
||||
glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })),
|
||||
openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })),
|
||||
bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })),
|
||||
vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })),
|
||||
|
|
@ -234,7 +227,6 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
export const providerSettingsSchema = z.object({
|
||||
apiProvider: providerNamesSchema.optional(),
|
||||
...anthropicSchema.shape,
|
||||
...glamaSchema.shape,
|
||||
...openRouterSchema.shape,
|
||||
...bedrockSchema.shape,
|
||||
...vertexSchema.shape,
|
||||
|
|
@ -266,9 +258,6 @@ export const PROVIDER_SETTINGS_KEYS = keysOf<ProviderSettings>()([
|
|||
"apiKey",
|
||||
"anthropicBaseUrl",
|
||||
"anthropicUseAuthToken",
|
||||
// Glama
|
||||
"glamaModelId",
|
||||
"glamaApiKey",
|
||||
// OpenRouter
|
||||
"openRouterApiKey",
|
||||
"openRouterModelId",
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
|
||||
// https://glama.ai/models
|
||||
export const glamaDefaultModelId = "anthropic/claude-3-7-sonnet"
|
||||
|
||||
export const glamaDefaultModelInfo: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsComputerUse: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
description:
|
||||
"Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)",
|
||||
}
|
||||
|
||||
export const GLAMA_DEFAULT_TEMPERATURE = 0
|
||||
|
|
@ -3,7 +3,6 @@ export * from "./bedrock.js"
|
|||
export * from "./chutes.js"
|
||||
export * from "./deepseek.js"
|
||||
export * from "./gemini.js"
|
||||
export * from "./glama.js"
|
||||
export * from "./groq.js"
|
||||
export * from "./lite-llm.js"
|
||||
export * from "./lm-studio.js"
|
||||
|
|
|
|||
|
|
@ -14,13 +14,6 @@ export const handleUri = async (uri: vscode.Uri) => {
|
|||
}
|
||||
|
||||
switch (path) {
|
||||
case "/glama": {
|
||||
const code = query.get("code")
|
||||
if (code) {
|
||||
await visibleProvider.handleGlamaCallback(code)
|
||||
}
|
||||
break
|
||||
}
|
||||
case "/openrouter": {
|
||||
const code = query.get("code")
|
||||
if (code) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import type { ProviderSettings, ModelInfo } from "@roo-code/types"
|
|||
import { ApiStream } from "./transform/stream"
|
||||
|
||||
import {
|
||||
GlamaHandler,
|
||||
AnthropicHandler,
|
||||
AwsBedrockHandler,
|
||||
OpenRouterHandler,
|
||||
|
|
@ -64,8 +63,6 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
switch (apiProvider) {
|
||||
case "anthropic":
|
||||
return new AnthropicHandler(options)
|
||||
case "glama":
|
||||
return new GlamaHandler(options)
|
||||
case "openrouter":
|
||||
return new OpenRouterHandler(options)
|
||||
case "bedrock":
|
||||
|
|
|
|||
|
|
@ -1,233 +0,0 @@
|
|||
// npx jest src/api/providers/__tests__/glama.test.ts
|
||||
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
import { GlamaHandler } from "../glama"
|
||||
import { ApiHandlerOptions } from "../../../shared/api"
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("../fetchers/modelCache", () => ({
|
||||
getModels: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
"anthropic/claude-3-7-sonnet": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3,
|
||||
outputPrice: 15,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
description: "Claude 3.7 Sonnet",
|
||||
thinking: false,
|
||||
supportsComputerUse: true,
|
||||
},
|
||||
"openai/gpt-4o": {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 128000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 5,
|
||||
outputPrice: 15,
|
||||
description: "GPT-4o",
|
||||
},
|
||||
})
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock OpenAI client
|
||||
const mockCreate = jest.fn()
|
||||
const mockWithResponse = jest.fn()
|
||||
|
||||
jest.mock("openai", () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: jest.fn().mockImplementation(() => ({
|
||||
chat: {
|
||||
completions: {
|
||||
create: (...args: any[]) => {
|
||||
const stream = {
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [{ delta: { content: "Test response" }, index: 0 }],
|
||||
usage: null,
|
||||
}
|
||||
yield {
|
||||
choices: [{ delta: {}, index: 0 }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const result = mockCreate(...args)
|
||||
|
||||
if (args[0].stream) {
|
||||
mockWithResponse.mockReturnValue(
|
||||
Promise.resolve({
|
||||
data: stream,
|
||||
response: {
|
||||
headers: {
|
||||
get: (name: string) =>
|
||||
name === "x-completion-request-id" ? "test-request-id" : null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
result.withResponse = mockWithResponse
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
describe("GlamaHandler", () => {
|
||||
let handler: GlamaHandler
|
||||
let mockOptions: ApiHandlerOptions
|
||||
|
||||
beforeEach(() => {
|
||||
mockOptions = {
|
||||
glamaApiKey: "test-api-key",
|
||||
glamaModelId: "anthropic/claude-3-7-sonnet",
|
||||
}
|
||||
|
||||
handler = new GlamaHandler(mockOptions)
|
||||
mockCreate.mockClear()
|
||||
mockWithResponse.mockClear()
|
||||
|
||||
// Default mock implementation for non-streaming responses
|
||||
mockCreate.mockResolvedValue({
|
||||
id: "test-completion",
|
||||
choices: [
|
||||
{
|
||||
message: { role: "assistant", content: "Test response" },
|
||||
finish_reason: "stop",
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
total_tokens: 15,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe("constructor", () => {
|
||||
it("should initialize with provided options", () => {
|
||||
expect(handler).toBeInstanceOf(GlamaHandler)
|
||||
expect(handler.getModel().id).toBe(mockOptions.glamaModelId)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createMessage", () => {
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: "Hello!",
|
||||
},
|
||||
]
|
||||
|
||||
it("should handle streaming responses", async () => {
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
expect(chunks.length).toBe(1)
|
||||
expect(chunks[0]).toEqual({ type: "text", text: "Test response" })
|
||||
})
|
||||
|
||||
it("should handle API errors", async () => {
|
||||
mockCreate.mockImplementationOnce(() => {
|
||||
throw new Error("API Error")
|
||||
})
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks = []
|
||||
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
fail("Expected error to be thrown")
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(Error)
|
||||
expect(error.message).toBe("API Error")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("completePrompt", () => {
|
||||
it("should complete prompt successfully", async () => {
|
||||
const result = await handler.completePrompt("Test prompt")
|
||||
expect(result).toBe("Test response")
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: mockOptions.glamaModelId,
|
||||
messages: [{ role: "user", content: "Test prompt" }],
|
||||
temperature: 0,
|
||||
max_tokens: 8192,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle API errors", async () => {
|
||||
mockCreate.mockRejectedValueOnce(new Error("API Error"))
|
||||
await expect(handler.completePrompt("Test prompt")).rejects.toThrow("Glama completion error: API Error")
|
||||
})
|
||||
|
||||
it("should handle empty response", async () => {
|
||||
mockCreate.mockResolvedValueOnce({
|
||||
choices: [{ message: { content: "" } }],
|
||||
})
|
||||
const result = await handler.completePrompt("Test prompt")
|
||||
expect(result).toBe("")
|
||||
})
|
||||
|
||||
it("should not set max_tokens for non-Anthropic models", async () => {
|
||||
// Reset mock to clear any previous calls
|
||||
mockCreate.mockClear()
|
||||
|
||||
const nonAnthropicOptions = {
|
||||
glamaApiKey: "test-key",
|
||||
glamaModelId: "openai/gpt-4o",
|
||||
}
|
||||
|
||||
const nonAnthropicHandler = new GlamaHandler(nonAnthropicOptions)
|
||||
|
||||
await nonAnthropicHandler.completePrompt("Test prompt")
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [{ role: "user", content: "Test prompt" }],
|
||||
temperature: 0,
|
||||
}),
|
||||
)
|
||||
expect(mockCreate.mock.calls[0][0]).not.toHaveProperty("max_tokens")
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetchModel", () => {
|
||||
it("should return model info", async () => {
|
||||
const modelInfo = await handler.fetchModel()
|
||||
expect(modelInfo.id).toBe(mockOptions.glamaModelId)
|
||||
expect(modelInfo.info).toBeDefined()
|
||||
expect(modelInfo.info.maxTokens).toBe(8192)
|
||||
expect(modelInfo.info.contextWindow).toBe(200_000)
|
||||
})
|
||||
|
||||
it("should return default model when invalid model provided", async () => {
|
||||
const handlerWithInvalidModel = new GlamaHandler({ ...mockOptions, glamaModelId: "invalid/model" })
|
||||
const modelInfo = await handlerWithInvalidModel.fetchModel()
|
||||
expect(modelInfo.id).toBe("anthropic/claude-3-7-sonnet")
|
||||
expect(modelInfo.info).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -2,7 +2,6 @@ import { getModels } from "../modelCache"
|
|||
import { getLiteLLMModels } from "../litellm"
|
||||
import { getOpenRouterModels } from "../openrouter"
|
||||
import { getRequestyModels } from "../requesty"
|
||||
import { getGlamaModels } from "../glama"
|
||||
import { getUnboundModels } from "../unbound"
|
||||
|
||||
// Mock NodeCache to avoid cache interference
|
||||
|
|
@ -25,13 +24,11 @@ jest.mock("fs/promises", () => ({
|
|||
jest.mock("../litellm")
|
||||
jest.mock("../openrouter")
|
||||
jest.mock("../requesty")
|
||||
jest.mock("../glama")
|
||||
jest.mock("../unbound")
|
||||
|
||||
const mockGetLiteLLMModels = getLiteLLMModels as jest.MockedFunction<typeof getLiteLLMModels>
|
||||
const mockGetOpenRouterModels = getOpenRouterModels as jest.MockedFunction<typeof getOpenRouterModels>
|
||||
const mockGetRequestyModels = getRequestyModels as jest.MockedFunction<typeof getRequestyModels>
|
||||
const mockGetGlamaModels = getGlamaModels as jest.MockedFunction<typeof getGlamaModels>
|
||||
const mockGetUnboundModels = getUnboundModels as jest.MockedFunction<typeof getUnboundModels>
|
||||
|
||||
const DUMMY_REQUESTY_KEY = "requesty-key-for-testing"
|
||||
|
|
@ -97,23 +94,6 @@ describe("getModels with new GetModelsOptions", () => {
|
|||
expect(result).toEqual(mockModels)
|
||||
})
|
||||
|
||||
it("calls getGlamaModels for glama provider", async () => {
|
||||
const mockModels = {
|
||||
"glama/model": {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
description: "Glama model",
|
||||
},
|
||||
}
|
||||
mockGetGlamaModels.mockResolvedValue(mockModels)
|
||||
|
||||
const result = await getModels({ provider: "glama" })
|
||||
|
||||
expect(mockGetGlamaModels).toHaveBeenCalled()
|
||||
expect(result).toEqual(mockModels)
|
||||
})
|
||||
|
||||
it("calls getUnboundModels with optional API key", async () => {
|
||||
const mockModels = {
|
||||
"unbound/model": {
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
import axios from "axios"
|
||||
|
||||
import type { ModelInfo } from "@roo-code/types"
|
||||
|
||||
import { parseApiPrice } from "../../../shared/cost"
|
||||
|
||||
export async function getGlamaModels(): Promise<Record<string, ModelInfo>> {
|
||||
const models: Record<string, ModelInfo> = {}
|
||||
|
||||
try {
|
||||
const response = await axios.get("https://glama.ai/api/gateway/v1/models")
|
||||
const rawModels = response.data
|
||||
|
||||
for (const rawModel of rawModels) {
|
||||
const modelInfo: ModelInfo = {
|
||||
maxTokens: rawModel.maxTokensOutput,
|
||||
contextWindow: rawModel.maxTokensInput,
|
||||
supportsImages: rawModel.capabilities?.includes("input:image"),
|
||||
supportsComputerUse: rawModel.capabilities?.includes("computer_use"),
|
||||
supportsPromptCache: rawModel.capabilities?.includes("caching"),
|
||||
inputPrice: parseApiPrice(rawModel.pricePerToken?.input),
|
||||
outputPrice: parseApiPrice(rawModel.pricePerToken?.output),
|
||||
description: undefined,
|
||||
cacheWritesPrice: parseApiPrice(rawModel.pricePerToken?.cacheWrite),
|
||||
cacheReadsPrice: parseApiPrice(rawModel.pricePerToken?.cacheRead),
|
||||
}
|
||||
|
||||
switch (rawModel.id) {
|
||||
case rawModel.id.startsWith("anthropic/"):
|
||||
modelInfo.maxTokens = 8192
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
models[rawModel.id] = modelInfo
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fetching Glama models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`)
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import { fileExistsAtPath } from "../../../utils/fs"
|
|||
|
||||
import { getOpenRouterModels } from "./openrouter"
|
||||
import { getRequestyModels } from "./requesty"
|
||||
import { getGlamaModels } from "./glama"
|
||||
import { getUnboundModels } from "./unbound"
|
||||
import { getLiteLLMModels } from "./litellm"
|
||||
import { GetModelsOptions } from "../../../shared/api"
|
||||
|
|
@ -57,9 +56,6 @@ export const getModels = async (options: GetModelsOptions): Promise<ModelRecord>
|
|||
// Requesty models endpoint requires an API key for per-user custom policies
|
||||
models = await getRequestyModels(options.apiKey)
|
||||
break
|
||||
case "glama":
|
||||
models = await getGlamaModels()
|
||||
break
|
||||
case "unbound":
|
||||
// Unbound models endpoint requires an API key to fetch application specific models
|
||||
models = await getUnboundModels(options.apiKey)
|
||||
|
|
|
|||
|
|
@ -1,146 +0,0 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import axios from "axios"
|
||||
import OpenAI from "openai"
|
||||
|
||||
import { glamaDefaultModelId, glamaDefaultModelInfo, GLAMA_DEFAULT_TEMPERATURE } from "@roo-code/types"
|
||||
|
||||
import { Package } from "../../shared/package"
|
||||
import { ApiHandlerOptions } from "../../shared/api"
|
||||
|
||||
import { ApiStream } from "../transform/stream"
|
||||
import { convertToOpenAiMessages } from "../transform/openai-format"
|
||||
import { addCacheBreakpoints } from "../transform/caching/anthropic"
|
||||
|
||||
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
|
||||
import { RouterProvider } from "./router-provider"
|
||||
|
||||
const DEFAULT_HEADERS = {
|
||||
"X-Glama-Metadata": JSON.stringify({
|
||||
labels: [{ key: "app", value: `vscode.${Package.publisher}.${Package.name}` }],
|
||||
}),
|
||||
}
|
||||
|
||||
export class GlamaHandler extends RouterProvider implements SingleCompletionHandler {
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
super({
|
||||
options,
|
||||
name: "glama",
|
||||
baseURL: "https://glama.ai/api/gateway/openai/v1",
|
||||
apiKey: options.glamaApiKey,
|
||||
modelId: options.glamaModelId,
|
||||
defaultModelId: glamaDefaultModelId,
|
||||
defaultModelInfo: glamaDefaultModelInfo,
|
||||
})
|
||||
}
|
||||
|
||||
override async *createMessage(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: ApiHandlerCreateMessageMetadata,
|
||||
): ApiStream {
|
||||
const { id: modelId, info } = await this.fetchModel()
|
||||
|
||||
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...convertToOpenAiMessages(messages),
|
||||
]
|
||||
|
||||
if (modelId.startsWith("anthropic/claude-3")) {
|
||||
addCacheBreakpoints(systemPrompt, openAiMessages)
|
||||
}
|
||||
|
||||
// Required by Anthropic; other providers default to max tokens allowed.
|
||||
let maxTokens: number | undefined
|
||||
|
||||
if (modelId.startsWith("anthropic/")) {
|
||||
maxTokens = info.maxTokens ?? undefined
|
||||
}
|
||||
|
||||
const requestOptions: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: modelId,
|
||||
max_tokens: maxTokens,
|
||||
messages: openAiMessages,
|
||||
stream: true,
|
||||
}
|
||||
|
||||
if (this.supportsTemperature(modelId)) {
|
||||
requestOptions.temperature = this.options.modelTemperature ?? GLAMA_DEFAULT_TEMPERATURE
|
||||
}
|
||||
|
||||
const { data: completion, response } = await this.client.chat.completions
|
||||
.create(requestOptions, { headers: DEFAULT_HEADERS })
|
||||
.withResponse()
|
||||
|
||||
const completionRequestId = response.headers.get("x-completion-request-id")
|
||||
|
||||
for await (const chunk of completion) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
|
||||
if (delta?.content) {
|
||||
yield { type: "text", text: delta.content }
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let attempt = 0
|
||||
|
||||
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
while (attempt++ < 10) {
|
||||
// In case of an interrupted request, we need to wait for the upstream API to finish processing the request
|
||||
// before we can fetch information about the token usage and cost.
|
||||
const response = await axios.get(
|
||||
`https://glama.ai/api/gateway/v1/completion-requests/${completionRequestId}`,
|
||||
{ headers: { Authorization: `Bearer ${this.options.glamaApiKey}` } },
|
||||
)
|
||||
|
||||
const completionRequest = response.data
|
||||
|
||||
if (completionRequest.tokenUsage && completionRequest.totalCostUsd) {
|
||||
yield {
|
||||
type: "usage",
|
||||
cacheWriteTokens: completionRequest.tokenUsage.cacheCreationInputTokens,
|
||||
cacheReadTokens: completionRequest.tokenUsage.cacheReadInputTokens,
|
||||
inputTokens: completionRequest.tokenUsage.promptTokens,
|
||||
outputTokens: completionRequest.tokenUsage.completionTokens,
|
||||
totalCost: parseFloat(completionRequest.totalCostUsd),
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
await delay(200)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching Glama completion details", error)
|
||||
}
|
||||
}
|
||||
|
||||
async completePrompt(prompt: string): Promise<string> {
|
||||
const { id: modelId, info } = await this.fetchModel()
|
||||
|
||||
try {
|
||||
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
|
||||
model: modelId,
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
}
|
||||
|
||||
if (this.supportsTemperature(modelId)) {
|
||||
requestOptions.temperature = this.options.modelTemperature ?? GLAMA_DEFAULT_TEMPERATURE
|
||||
}
|
||||
|
||||
if (modelId.startsWith("anthropic/")) {
|
||||
requestOptions.max_tokens = info.maxTokens
|
||||
}
|
||||
|
||||
const response = await this.client.chat.completions.create(requestOptions)
|
||||
return response.choices[0]?.message.content || ""
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Glama completion error: ${error.message}`)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ export { ChutesHandler } from "./chutes"
|
|||
export { DeepSeekHandler } from "./deepseek"
|
||||
export { FakeAIHandler } from "./fake-ai"
|
||||
export { GeminiHandler } from "./gemini"
|
||||
export { GlamaHandler } from "./glama"
|
||||
export { GroqHandler } from "./groq"
|
||||
export { HumanRelayHandler } from "./human-relay"
|
||||
export { LiteLLMHandler } from "./lite-llm"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {
|
|||
type CloudUserInfo,
|
||||
requestyDefaultModelId,
|
||||
openRouterDefaultModelId,
|
||||
glamaDefaultModelId,
|
||||
ORGANIZATION_ALLOW_ALL,
|
||||
} from "@roo-code/types"
|
||||
import { TelemetryService } from "@roo-code/telemetry"
|
||||
|
|
@ -1053,36 +1052,6 @@ export class ClineProvider
|
|||
await this.upsertProviderProfile(currentApiConfigName, newConfiguration)
|
||||
}
|
||||
|
||||
// Glama
|
||||
|
||||
async handleGlamaCallback(code: string) {
|
||||
let apiKey: string
|
||||
try {
|
||||
const response = await axios.post("https://glama.ai/api/gateway/v1/auth/exchange-code", { code })
|
||||
if (response.data && response.data.apiKey) {
|
||||
apiKey = response.data.apiKey
|
||||
} else {
|
||||
throw new Error("Invalid response from Glama API")
|
||||
}
|
||||
} catch (error) {
|
||||
this.log(
|
||||
`Error exchanging code for API key: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
const { apiConfiguration, currentApiConfigName } = await this.getState()
|
||||
|
||||
const newConfiguration: ProviderSettings = {
|
||||
...apiConfiguration,
|
||||
apiProvider: "glama",
|
||||
glamaApiKey: apiKey,
|
||||
glamaModelId: apiConfiguration?.glamaModelId || glamaDefaultModelId,
|
||||
}
|
||||
|
||||
await this.upsertProviderProfile(currentApiConfigName, newConfiguration)
|
||||
}
|
||||
|
||||
// Requesty
|
||||
|
||||
async handleRequestyCallback(code: string) {
|
||||
|
|
|
|||
|
|
@ -2241,7 +2241,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
litellmApiKey: "litellm-key",
|
||||
litellmBaseUrl: "http://localhost:4000",
|
||||
|
|
@ -2261,7 +2260,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
// Verify getModels was called for each provider with correct options
|
||||
expect(getModels).toHaveBeenCalledWith({ provider: "openrouter" })
|
||||
expect(getModels).toHaveBeenCalledWith({ provider: "requesty", apiKey: "requesty-key" })
|
||||
expect(getModels).toHaveBeenCalledWith({ provider: "glama" })
|
||||
expect(getModels).toHaveBeenCalledWith({ provider: "unbound", apiKey: "unbound-key" })
|
||||
expect(getModels).toHaveBeenCalledWith({
|
||||
provider: "litellm",
|
||||
|
|
@ -2275,7 +2273,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: mockModels,
|
||||
glama: mockModels,
|
||||
unbound: mockModels,
|
||||
litellm: mockModels,
|
||||
},
|
||||
|
|
@ -2290,7 +2287,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
litellmApiKey: "litellm-key",
|
||||
litellmBaseUrl: "http://localhost:4000",
|
||||
|
|
@ -2304,7 +2300,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
getModels
|
||||
.mockResolvedValueOnce(mockModels) // openrouter success
|
||||
.mockRejectedValueOnce(new Error("Requesty API error")) // requesty fail
|
||||
.mockResolvedValueOnce(mockModels) // glama success
|
||||
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound fail
|
||||
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm fail
|
||||
|
||||
|
|
@ -2316,7 +2311,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: {},
|
||||
glama: mockModels,
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -2354,7 +2348,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
// No litellm config
|
||||
},
|
||||
|
|
@ -2388,7 +2381,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
// No litellm config
|
||||
},
|
||||
|
|
@ -2413,7 +2405,6 @@ describe("ClineProvider - Router Models", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: mockModels,
|
||||
glama: mockModels,
|
||||
unbound: mockModels,
|
||||
litellm: {},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
litellmApiKey: "litellm-key",
|
||||
litellmBaseUrl: "http://localhost:4000",
|
||||
|
|
@ -53,7 +52,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
// Verify getModels was called for each provider
|
||||
expect(mockGetModels).toHaveBeenCalledWith({ provider: "openrouter" })
|
||||
expect(mockGetModels).toHaveBeenCalledWith({ provider: "requesty", apiKey: "requesty-key" })
|
||||
expect(mockGetModels).toHaveBeenCalledWith({ provider: "glama" })
|
||||
expect(mockGetModels).toHaveBeenCalledWith({ provider: "unbound", apiKey: "unbound-key" })
|
||||
expect(mockGetModels).toHaveBeenCalledWith({
|
||||
provider: "litellm",
|
||||
|
|
@ -67,7 +65,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: mockModels,
|
||||
glama: mockModels,
|
||||
unbound: mockModels,
|
||||
litellm: mockModels,
|
||||
},
|
||||
|
|
@ -79,7 +76,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
// Missing litellm config
|
||||
},
|
||||
|
|
@ -117,7 +113,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
apiConfiguration: {
|
||||
openRouterApiKey: "openrouter-key",
|
||||
requestyApiKey: "requesty-key",
|
||||
glamaApiKey: "glama-key",
|
||||
unboundApiKey: "unbound-key",
|
||||
// Missing litellm config
|
||||
},
|
||||
|
|
@ -152,7 +147,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: mockModels,
|
||||
glama: mockModels,
|
||||
unbound: mockModels,
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -173,7 +167,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
mockGetModels
|
||||
.mockResolvedValueOnce(mockModels) // openrouter
|
||||
.mockRejectedValueOnce(new Error("Requesty API error")) // requesty
|
||||
.mockResolvedValueOnce(mockModels) // glama
|
||||
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound
|
||||
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm
|
||||
|
||||
|
|
@ -187,7 +180,6 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
routerModels: {
|
||||
openrouter: mockModels,
|
||||
requesty: {},
|
||||
glama: mockModels,
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -221,9 +213,8 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
mockGetModels
|
||||
.mockRejectedValueOnce(new Error("Structured error message")) // Error object
|
||||
.mockRejectedValueOnce("String error message") // String error
|
||||
.mockRejectedValueOnce({ message: "Object with message" }) // Object error
|
||||
.mockResolvedValueOnce({}) // Success
|
||||
.mockResolvedValueOnce({}) // Success
|
||||
.mockRejectedValueOnce(new Error("Unbound API error")) // unbound
|
||||
.mockRejectedValueOnce(new Error("LiteLLM connection failed")) // litellm
|
||||
|
||||
await webviewMessageHandler(mockClineProvider, {
|
||||
type: "requestRouterModels",
|
||||
|
|
@ -247,8 +238,15 @@ describe("webviewMessageHandler - requestRouterModels", () => {
|
|||
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
|
||||
type: "singleRouterModelFetchResponse",
|
||||
success: false,
|
||||
error: "[object Object]",
|
||||
values: { provider: "glama" },
|
||||
error: "Unbound API error",
|
||||
values: { provider: "unbound" },
|
||||
})
|
||||
|
||||
expect(mockClineProvider.postMessageToWebview).toHaveBeenCalledWith({
|
||||
type: "singleRouterModelFetchResponse",
|
||||
success: false,
|
||||
error: "LiteLLM connection failed",
|
||||
values: { provider: "litellm" },
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -300,7 +300,6 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
|
|||
const routerModels: Partial<Record<RouterName, ModelRecord>> = {
|
||||
openrouter: {},
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
}
|
||||
|
|
@ -320,7 +319,6 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
|
|||
const modelFetchPromises: Array<{ key: RouterName; options: GetModelsOptions }> = [
|
||||
{ key: "openrouter", options: { provider: "openrouter" } },
|
||||
{ key: "requesty", options: { provider: "requesty", apiKey: apiConfiguration.requestyApiKey } },
|
||||
{ key: "glama", options: { provider: "glama" } },
|
||||
{ key: "unbound", options: { provider: "unbound", apiKey: apiConfiguration.unboundApiKey } },
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -78,8 +78,6 @@ export class ProfileValidator {
|
|||
return profile.vsCodeLmModelSelector?.id
|
||||
case "openrouter":
|
||||
return profile.openRouterModelId
|
||||
case "glama":
|
||||
return profile.glamaModelId
|
||||
case "ollama":
|
||||
return profile.ollamaModelId
|
||||
case "requesty":
|
||||
|
|
|
|||
|
|
@ -286,21 +286,6 @@ describe("ProfileValidator", () => {
|
|||
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
||||
})
|
||||
|
||||
it("should extract glamaModelId for glama provider", () => {
|
||||
const allowList: OrganizationAllowList = {
|
||||
allowAll: false,
|
||||
providers: {
|
||||
glama: { allowAll: false, models: ["glama-model"] },
|
||||
},
|
||||
}
|
||||
const profile: ProviderSettings = {
|
||||
apiProvider: "glama",
|
||||
glamaModelId: "glama-model",
|
||||
}
|
||||
|
||||
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
||||
})
|
||||
|
||||
it("should extract requestyModelId for requesty provider", () => {
|
||||
const allowList: OrganizationAllowList = {
|
||||
allowAll: false,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ describe("checkExistKey", () => {
|
|||
it("should return true when multiple keys are defined", () => {
|
||||
const config: ProviderSettings = {
|
||||
apiKey: "test-key",
|
||||
glamaApiKey: "glama-key",
|
||||
openRouterApiKey: "openrouter-key",
|
||||
}
|
||||
expect(checkExistKey(config)).toBe(true)
|
||||
|
|
@ -43,7 +42,6 @@ describe("checkExistKey", () => {
|
|||
it("should return false when all key fields are undefined", () => {
|
||||
const config: ProviderSettings = {
|
||||
apiKey: undefined,
|
||||
glamaApiKey: undefined,
|
||||
openRouterApiKey: undefined,
|
||||
awsRegion: undefined,
|
||||
vertexProjectId: undefined,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider">
|
|||
|
||||
// RouterName
|
||||
|
||||
const routerNames = ["openrouter", "requesty", "glama", "unbound", "litellm"] as const
|
||||
const routerNames = ["openrouter", "requesty", "unbound", "litellm"] as const
|
||||
|
||||
export type RouterName = (typeof routerNames)[number]
|
||||
|
||||
|
|
@ -78,7 +78,6 @@ export const getModelMaxOutputTokens = ({
|
|||
|
||||
export type GetModelsOptions =
|
||||
| { provider: "openrouter" }
|
||||
| { provider: "glama" }
|
||||
| { provider: "requesty"; apiKey?: string }
|
||||
| { provider: "unbound"; apiKey?: string }
|
||||
| { provider: "litellm"; apiKey: string; baseUrl: string }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
type ProviderSettings,
|
||||
openRouterDefaultModelId,
|
||||
requestyDefaultModelId,
|
||||
glamaDefaultModelId,
|
||||
unboundDefaultModelId,
|
||||
litellmDefaultModelId,
|
||||
} from "@roo-code/types"
|
||||
|
|
@ -28,7 +27,6 @@ import {
|
|||
Chutes,
|
||||
DeepSeek,
|
||||
Gemini,
|
||||
Glama,
|
||||
Groq,
|
||||
LMStudio,
|
||||
LiteLLM,
|
||||
|
|
@ -199,7 +197,7 @@ const ApiOptions = ({
|
|||
(value: ProviderName) => {
|
||||
// It would be much easier to have a single attribute that stores
|
||||
// the modelId, but we have a separate attribute for each of
|
||||
// OpenRouter, Glama, Unbound, and Requesty.
|
||||
// OpenRouter, Unbound, and Requesty.
|
||||
// If you switch to one of these providers and the corresponding
|
||||
// modelId is not set then you immediately end up in an error state.
|
||||
// To address that we set the modelId to the default value for th
|
||||
|
|
@ -210,11 +208,6 @@ const ApiOptions = ({
|
|||
setApiConfigurationField("openRouterModelId", openRouterDefaultModelId)
|
||||
}
|
||||
break
|
||||
case "glama":
|
||||
if (!apiConfiguration.glamaModelId) {
|
||||
setApiConfigurationField("glamaModelId", glamaDefaultModelId)
|
||||
}
|
||||
break
|
||||
case "unbound":
|
||||
if (!apiConfiguration.unboundModelId) {
|
||||
setApiConfigurationField("unboundModelId", unboundDefaultModelId)
|
||||
|
|
@ -237,7 +230,6 @@ const ApiOptions = ({
|
|||
[
|
||||
setApiConfigurationField,
|
||||
apiConfiguration.openRouterModelId,
|
||||
apiConfiguration.glamaModelId,
|
||||
apiConfiguration.unboundModelId,
|
||||
apiConfiguration.requestyModelId,
|
||||
apiConfiguration.litellmModelId,
|
||||
|
|
@ -316,16 +308,6 @@ const ApiOptions = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "glama" && (
|
||||
<Glama
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
routerModels={routerModels}
|
||||
uriScheme={uriScheme}
|
||||
organizationAllowList={organizationAllowList}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "unbound" && (
|
||||
<Unbound
|
||||
apiConfiguration={apiConfiguration}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { ModelInfoView } from "./ModelInfoView"
|
|||
|
||||
type ModelIdKey = keyof Pick<
|
||||
ProviderSettings,
|
||||
"glamaModelId" | "openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId" | "litellmModelId"
|
||||
"openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId" | "litellmModelId"
|
||||
>
|
||||
|
||||
interface ModelPickerProps {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ describe("ModelPicker", () => {
|
|||
const defaultProps = {
|
||||
apiConfiguration: {},
|
||||
defaultModelId: "model1",
|
||||
modelIdKey: "glamaModelId" as const,
|
||||
modelIdKey: "openRouterModelId" as const,
|
||||
serviceName: "Test Service",
|
||||
serviceUrl: "https://test.service",
|
||||
recommendedModel: "recommended-model",
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ export const PROVIDERS = [
|
|||
{ value: "openai", label: "OpenAI Compatible" },
|
||||
{ value: "vertex", label: "GCP Vertex AI" },
|
||||
{ value: "bedrock", label: "Amazon Bedrock" },
|
||||
{ value: "glama", label: "Glama" },
|
||||
{ value: "vscode-lm", label: "VS Code LM API" },
|
||||
{ value: "mistral", label: "Mistral" },
|
||||
{ value: "lmstudio", label: "LM Studio" },
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
import { useCallback } from "react"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { type ProviderSettings, type OrganizationAllowList, glamaDefaultModelId } from "@roo-code/types"
|
||||
|
||||
import type { RouterModels } from "@roo/api"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { getGlamaAuthUrl } from "@src/oauth/urls"
|
||||
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
|
||||
|
||||
import { inputEventTransform } from "../transforms"
|
||||
import { ModelPicker } from "../ModelPicker"
|
||||
|
||||
type GlamaProps = {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
routerModels?: RouterModels
|
||||
uriScheme?: string
|
||||
organizationAllowList: OrganizationAllowList
|
||||
}
|
||||
|
||||
export const Glama = ({
|
||||
apiConfiguration,
|
||||
setApiConfigurationField,
|
||||
routerModels,
|
||||
uriScheme,
|
||||
organizationAllowList,
|
||||
}: GlamaProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
<K extends keyof ProviderSettings, E>(
|
||||
field: K,
|
||||
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
|
||||
) =>
|
||||
(event: E | Event) => {
|
||||
setApiConfigurationField(field, transform(event as E))
|
||||
},
|
||||
[setApiConfigurationField],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.glamaApiKey || ""}
|
||||
type="password"
|
||||
onInput={handleInputChange("glamaApiKey")}
|
||||
placeholder={t("settings:placeholders.apiKey")}
|
||||
className="w-full">
|
||||
<label className="block font-medium mb-1">{t("settings:providers.glamaApiKey")}</label>
|
||||
</VSCodeTextField>
|
||||
<div className="text-sm text-vscode-descriptionForeground -mt-2">
|
||||
{t("settings:providers.apiKeyStorageNotice")}
|
||||
</div>
|
||||
{!apiConfiguration?.glamaApiKey && (
|
||||
<VSCodeButtonLink href={getGlamaAuthUrl(uriScheme)} style={{ width: "100%" }} appearance="primary">
|
||||
{t("settings:providers.getGlamaApiKey")}
|
||||
</VSCodeButtonLink>
|
||||
)}
|
||||
<ModelPicker
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
defaultModelId={glamaDefaultModelId}
|
||||
models={routerModels?.glama ?? {}}
|
||||
modelIdKey="glamaModelId"
|
||||
serviceName="Glama"
|
||||
serviceUrl="https://glama.ai/models"
|
||||
organizationAllowList={organizationAllowList}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ export { Bedrock } from "./Bedrock"
|
|||
export { Chutes } from "./Chutes"
|
||||
export { DeepSeek } from "./DeepSeek"
|
||||
export { Gemini } from "./Gemini"
|
||||
export { Glama } from "./Glama"
|
||||
export { Groq } from "./Groq"
|
||||
export { LMStudio } from "./LMStudio"
|
||||
export { Mistral } from "./Mistral"
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ describe("useSelectedModel", () => {
|
|||
"test-model": baseModelInfo,
|
||||
},
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -108,7 +107,6 @@ describe("useSelectedModel", () => {
|
|||
data: {
|
||||
openrouter: {},
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -162,7 +160,6 @@ describe("useSelectedModel", () => {
|
|||
"test-model": baseModelInfo,
|
||||
},
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -217,7 +214,6 @@ describe("useSelectedModel", () => {
|
|||
data: {
|
||||
openrouter: { "test-model": baseModelInfo },
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -261,7 +257,6 @@ describe("useSelectedModel", () => {
|
|||
},
|
||||
},
|
||||
requesty: {},
|
||||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
},
|
||||
|
|
@ -321,7 +316,7 @@ describe("useSelectedModel", () => {
|
|||
|
||||
it("should return loading state when open router model providers are loading", () => {
|
||||
mockUseRouterModels.mockReturnValue({
|
||||
data: { openrouter: {}, requesty: {}, glama: {}, unbound: {}, litellm: {} },
|
||||
data: { openrouter: {}, requesty: {}, unbound: {}, litellm: {} },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
} as any)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import {
|
|||
vscodeLlmDefaultModelId,
|
||||
openRouterDefaultModelId,
|
||||
requestyDefaultModelId,
|
||||
glamaDefaultModelId,
|
||||
unboundDefaultModelId,
|
||||
litellmDefaultModelId,
|
||||
} from "@roo-code/types"
|
||||
|
|
@ -102,11 +101,6 @@ function getSelectedModel({
|
|||
? { id, info }
|
||||
: { id: requestyDefaultModelId, info: routerModels.requesty[requestyDefaultModelId] }
|
||||
}
|
||||
case "glama": {
|
||||
const id = apiConfiguration.glamaModelId ?? glamaDefaultModelId
|
||||
const info = routerModels.glama[id]
|
||||
return info ? { id, info } : { id: glamaDefaultModelId, info: routerModels.glama[glamaDefaultModelId] }
|
||||
}
|
||||
case "unbound": {
|
||||
const id = apiConfiguration.unboundModelId ?? unboundDefaultModelId
|
||||
const info = routerModels.unbound[id]
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Clau API d'OpenRouter",
|
||||
"getOpenRouterApiKey": "Obtenir clau API d'OpenRouter",
|
||||
"apiKeyStorageNotice": "Les claus API s'emmagatzemen de forma segura a l'Emmagatzematge Secret de VSCode",
|
||||
"glamaApiKey": "Clau API de Glama",
|
||||
"getGlamaApiKey": "Obtenir clau API de Glama",
|
||||
"useCustomBaseUrl": "Utilitzar URL base personalitzada",
|
||||
"useReasoning": "Activar raonament",
|
||||
"useHostHeader": "Utilitzar capçalera Host personalitzada",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API-Schlüssel",
|
||||
"getOpenRouterApiKey": "OpenRouter API-Schlüssel erhalten",
|
||||
"apiKeyStorageNotice": "API-Schlüssel werden sicher im VSCode Secret Storage gespeichert",
|
||||
"glamaApiKey": "Glama API-Schlüssel",
|
||||
"getGlamaApiKey": "Glama API-Schlüssel erhalten",
|
||||
"useCustomBaseUrl": "Benutzerdefinierte Basis-URL verwenden",
|
||||
"useReasoning": "Reasoning aktivieren",
|
||||
"useHostHeader": "Benutzerdefinierten Host-Header verwenden",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API Key",
|
||||
"getOpenRouterApiKey": "Get OpenRouter API Key",
|
||||
"apiKeyStorageNotice": "API keys are stored securely in VSCode's Secret Storage",
|
||||
"glamaApiKey": "Glama API Key",
|
||||
"getGlamaApiKey": "Get Glama API Key",
|
||||
"useCustomBaseUrl": "Use custom base URL",
|
||||
"useReasoning": "Enable reasoning",
|
||||
"useHostHeader": "Use custom Host header",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Clave API de OpenRouter",
|
||||
"getOpenRouterApiKey": "Obtener clave API de OpenRouter",
|
||||
"apiKeyStorageNotice": "Las claves API se almacenan de forma segura en el Almacenamiento Secreto de VSCode",
|
||||
"glamaApiKey": "Clave API de Glama",
|
||||
"getGlamaApiKey": "Obtener clave API de Glama",
|
||||
"useCustomBaseUrl": "Usar URL base personalizada",
|
||||
"useReasoning": "Habilitar razonamiento",
|
||||
"useHostHeader": "Usar encabezado Host personalizado",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Clé API OpenRouter",
|
||||
"getOpenRouterApiKey": "Obtenir la clé API OpenRouter",
|
||||
"apiKeyStorageNotice": "Les clés API sont stockées en toute sécurité dans le stockage sécurisé de VSCode",
|
||||
"glamaApiKey": "Clé API Glama",
|
||||
"getGlamaApiKey": "Obtenir la clé API Glama",
|
||||
"useCustomBaseUrl": "Utiliser une URL de base personnalisée",
|
||||
"useReasoning": "Activer le raisonnement",
|
||||
"useHostHeader": "Utiliser un en-tête Host personnalisé",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API कुंजी",
|
||||
"getOpenRouterApiKey": "OpenRouter API कुंजी प्राप्त करें",
|
||||
"apiKeyStorageNotice": "API कुंजियाँ VSCode के सुरक्षित स्टोरेज में सुरक्षित रूप से संग्रहीत हैं",
|
||||
"glamaApiKey": "Glama API कुंजी",
|
||||
"getGlamaApiKey": "Glama API कुंजी प्राप्त करें",
|
||||
"useCustomBaseUrl": "कस्टम बेस URL का उपयोग करें",
|
||||
"useReasoning": "तर्क सक्षम करें",
|
||||
"useHostHeader": "कस्टम होस्ट हेडर का उपयोग करें",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Chiave API OpenRouter",
|
||||
"getOpenRouterApiKey": "Ottieni chiave API OpenRouter",
|
||||
"apiKeyStorageNotice": "Le chiavi API sono memorizzate in modo sicuro nell'Archivio Segreto di VSCode",
|
||||
"glamaApiKey": "Chiave API Glama",
|
||||
"getGlamaApiKey": "Ottieni chiave API Glama",
|
||||
"useCustomBaseUrl": "Usa URL base personalizzato",
|
||||
"useReasoning": "Abilita ragionamento",
|
||||
"useHostHeader": "Usa intestazione Host personalizzata",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter APIキー",
|
||||
"getOpenRouterApiKey": "OpenRouter APIキーを取得",
|
||||
"apiKeyStorageNotice": "APIキーはVSCodeのシークレットストレージに安全に保存されます",
|
||||
"glamaApiKey": "Glama APIキー",
|
||||
"getGlamaApiKey": "Glama APIキーを取得",
|
||||
"useCustomBaseUrl": "カスタムベースURLを使用",
|
||||
"useReasoning": "推論を有効化",
|
||||
"useHostHeader": "カスタムHostヘッダーを使用",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API 키",
|
||||
"getOpenRouterApiKey": "OpenRouter API 키 받기",
|
||||
"apiKeyStorageNotice": "API 키는 VSCode의 보안 저장소에 안전하게 저장됩니다",
|
||||
"glamaApiKey": "Glama API 키",
|
||||
"getGlamaApiKey": "Glama API 키 받기",
|
||||
"useCustomBaseUrl": "사용자 정의 기본 URL 사용",
|
||||
"useReasoning": "추론 활성화",
|
||||
"useHostHeader": "사용자 정의 Host 헤더 사용",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API-sleutel",
|
||||
"getOpenRouterApiKey": "OpenRouter API-sleutel ophalen",
|
||||
"apiKeyStorageNotice": "API-sleutels worden veilig opgeslagen in de geheime opslag van VSCode",
|
||||
"glamaApiKey": "Glama API-sleutel",
|
||||
"getGlamaApiKey": "Glama API-sleutel ophalen",
|
||||
"useCustomBaseUrl": "Aangepaste basis-URL gebruiken",
|
||||
"useReasoning": "Redenering inschakelen",
|
||||
"useHostHeader": "Aangepaste Host-header gebruiken",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Klucz API OpenRouter",
|
||||
"getOpenRouterApiKey": "Uzyskaj klucz API OpenRouter",
|
||||
"apiKeyStorageNotice": "Klucze API są bezpiecznie przechowywane w Tajnym Magazynie VSCode",
|
||||
"glamaApiKey": "Klucz API Glama",
|
||||
"getGlamaApiKey": "Uzyskaj klucz API Glama",
|
||||
"useCustomBaseUrl": "Użyj niestandardowego URL bazowego",
|
||||
"useReasoning": "Włącz rozumowanie",
|
||||
"useHostHeader": "Użyj niestandardowego nagłówka Host",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Chave de API OpenRouter",
|
||||
"getOpenRouterApiKey": "Obter chave de API OpenRouter",
|
||||
"apiKeyStorageNotice": "As chaves de API são armazenadas com segurança no Armazenamento Secreto do VSCode",
|
||||
"glamaApiKey": "Chave de API Glama",
|
||||
"getGlamaApiKey": "Obter chave de API Glama",
|
||||
"useCustomBaseUrl": "Usar URL base personalizado",
|
||||
"useReasoning": "Habilitar raciocínio",
|
||||
"useHostHeader": "Usar cabeçalho Host personalizado",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API-ключ",
|
||||
"getOpenRouterApiKey": "Получить OpenRouter API-ключ",
|
||||
"apiKeyStorageNotice": "API-ключи хранятся безопасно в Secret Storage VSCode",
|
||||
"glamaApiKey": "Glama API-ключ",
|
||||
"getGlamaApiKey": "Получить Glama API-ключ",
|
||||
"useCustomBaseUrl": "Использовать пользовательский базовый URL",
|
||||
"useReasoning": "Включить рассуждения",
|
||||
"useHostHeader": "Использовать пользовательский Host-заголовок",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API Anahtarı",
|
||||
"getOpenRouterApiKey": "OpenRouter API Anahtarı Al",
|
||||
"apiKeyStorageNotice": "API anahtarları VSCode'un Gizli Depolamasında güvenli bir şekilde saklanır",
|
||||
"glamaApiKey": "Glama API Anahtarı",
|
||||
"getGlamaApiKey": "Glama API Anahtarı Al",
|
||||
"useCustomBaseUrl": "Özel temel URL kullan",
|
||||
"useReasoning": "Akıl yürütmeyi etkinleştir",
|
||||
"useHostHeader": "Özel Host başlığı kullan",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "Khóa API OpenRouter",
|
||||
"getOpenRouterApiKey": "Lấy khóa API OpenRouter",
|
||||
"apiKeyStorageNotice": "Khóa API được lưu trữ an toàn trong Bộ lưu trữ bí mật của VSCode",
|
||||
"glamaApiKey": "Khóa API Glama",
|
||||
"getGlamaApiKey": "Lấy khóa API Glama",
|
||||
"useCustomBaseUrl": "Sử dụng URL cơ sở tùy chỉnh",
|
||||
"useReasoning": "Bật lý luận",
|
||||
"useHostHeader": "Sử dụng tiêu đề Host tùy chỉnh",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API 密钥",
|
||||
"getOpenRouterApiKey": "获取 OpenRouter API 密钥",
|
||||
"apiKeyStorageNotice": "API 密钥安全存储在 VSCode 的密钥存储中",
|
||||
"glamaApiKey": "Glama API 密钥",
|
||||
"getGlamaApiKey": "获取 Glama API 密钥",
|
||||
"useCustomBaseUrl": "使用自定义基础 URL",
|
||||
"useReasoning": "启用推理",
|
||||
"useHostHeader": "使用自定义 Host 标头",
|
||||
|
|
|
|||
|
|
@ -139,8 +139,6 @@
|
|||
"openRouterApiKey": "OpenRouter API 金鑰",
|
||||
"getOpenRouterApiKey": "取得 OpenRouter API 金鑰",
|
||||
"apiKeyStorageNotice": "API 金鑰安全儲存於 VSCode 金鑰儲存中",
|
||||
"glamaApiKey": "Glama API 金鑰",
|
||||
"getGlamaApiKey": "取得 Glama API 金鑰",
|
||||
"useCustomBaseUrl": "使用自訂基礎 URL",
|
||||
"useReasoning": "啟用推理",
|
||||
"useHostHeader": "使用自訂 Host 標頭",
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ export function getCallbackUrl(provider: string, uriScheme?: string) {
|
|||
return encodeURIComponent(`${uriScheme || "vscode"}://${Package.publisher}.${Package.name}/${provider}`)
|
||||
}
|
||||
|
||||
export function getGlamaAuthUrl(uriScheme?: string) {
|
||||
return `https://glama.ai/oauth/authorize?callback_url=${getCallbackUrl("glama", uriScheme)}`
|
||||
}
|
||||
|
||||
export function getOpenRouterAuthUrl(uriScheme?: string) {
|
||||
return `https://openrouter.ai/auth?callback_url=${getCallbackUrl("openrouter", uriScheme)}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,11 +32,6 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
|
|||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
case "glama":
|
||||
if (!apiConfiguration.glamaApiKey) {
|
||||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
case "unbound":
|
||||
if (!apiConfiguration.unboundApiKey) {
|
||||
return i18next.t("settings:validation.apiKey")
|
||||
|
|
@ -138,8 +133,6 @@ function getModelIdForProvider(apiConfiguration: ProviderSettings, provider: str
|
|||
switch (provider) {
|
||||
case "openrouter":
|
||||
return apiConfiguration.openRouterModelId
|
||||
case "glama":
|
||||
return apiConfiguration.glamaModelId
|
||||
case "unbound":
|
||||
return apiConfiguration.unboundModelId
|
||||
case "requesty":
|
||||
|
|
@ -207,9 +200,6 @@ export function validateModelId(apiConfiguration: ProviderSettings, routerModels
|
|||
case "openrouter":
|
||||
modelId = apiConfiguration.openRouterModelId
|
||||
break
|
||||
case "glama":
|
||||
modelId = apiConfiguration.glamaModelId
|
||||
break
|
||||
case "unbound":
|
||||
modelId = apiConfiguration.unboundModelId
|
||||
break
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue