mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add IO Intelligence Provider (#6875)
Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
This commit is contained in:
parent
bce579f4c1
commit
1018b885ab
42 changed files with 850 additions and 4 deletions
|
|
@ -194,6 +194,7 @@ export const SECRET_STATE_KEYS = [
|
|||
"huggingFaceApiKey",
|
||||
"sambaNovaApiKey",
|
||||
"fireworksApiKey",
|
||||
"ioIntelligenceApiKey",
|
||||
] as const satisfies readonly (keyof ProviderSettings)[]
|
||||
export type SecretState = Pick<ProviderSettings, (typeof SECRET_STATE_KEYS)[number]>
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const providerNames = [
|
|||
"sambanova",
|
||||
"zai",
|
||||
"fireworks",
|
||||
"io-intelligence",
|
||||
] as const
|
||||
|
||||
export const providerNamesSchema = z.enum(providerNames)
|
||||
|
|
@ -276,6 +277,11 @@ const fireworksSchema = apiModelIdProviderModelSchema.extend({
|
|||
fireworksApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const ioIntelligenceSchema = apiModelIdProviderModelSchema.extend({
|
||||
ioIntelligenceModelId: z.string().optional(),
|
||||
ioIntelligenceApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const defaultSchema = z.object({
|
||||
apiProvider: z.undefined(),
|
||||
})
|
||||
|
|
@ -311,6 +317,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
sambaNovaSchema.merge(z.object({ apiProvider: z.literal("sambanova") })),
|
||||
zaiSchema.merge(z.object({ apiProvider: z.literal("zai") })),
|
||||
fireworksSchema.merge(z.object({ apiProvider: z.literal("fireworks") })),
|
||||
ioIntelligenceSchema.merge(z.object({ apiProvider: z.literal("io-intelligence") })),
|
||||
defaultSchema,
|
||||
])
|
||||
|
||||
|
|
@ -346,6 +353,7 @@ export const providerSettingsSchema = z.object({
|
|||
...sambaNovaSchema.shape,
|
||||
...zaiSchema.shape,
|
||||
...fireworksSchema.shape,
|
||||
...ioIntelligenceSchema.shape,
|
||||
...codebaseIndexProviderSchema.shape,
|
||||
})
|
||||
|
||||
|
|
@ -371,6 +379,7 @@ export const MODEL_ID_KEYS: Partial<keyof ProviderSettings>[] = [
|
|||
"requestyModelId",
|
||||
"litellmModelId",
|
||||
"huggingFaceModelId",
|
||||
"ioIntelligenceModelId",
|
||||
]
|
||||
|
||||
export const getModelId = (settings: ProviderSettings): string | undefined => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export * from "./gemini.js"
|
|||
export * from "./glama.js"
|
||||
export * from "./groq.js"
|
||||
export * from "./huggingface.js"
|
||||
export * from "./io-intelligence.js"
|
||||
export * from "./lite-llm.js"
|
||||
export * from "./lm-studio.js"
|
||||
export * from "./mistral.js"
|
||||
|
|
|
|||
44
packages/types/src/providers/io-intelligence.ts
Normal file
44
packages/types/src/providers/io-intelligence.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
|
||||
export type IOIntelligenceModelId =
|
||||
| "deepseek-ai/DeepSeek-R1-0528"
|
||||
| "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
|
||||
| "Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar"
|
||||
| "openai/gpt-oss-120b"
|
||||
|
||||
export const ioIntelligenceDefaultModelId: IOIntelligenceModelId = "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
|
||||
|
||||
export const ioIntelligenceDefaultBaseUrl = "https://api.intelligence.io.solutions/api/v1"
|
||||
|
||||
export const IO_INTELLIGENCE_CACHE_DURATION = 1000 * 60 * 60 // 1 hour
|
||||
|
||||
export const ioIntelligenceModels = {
|
||||
"deepseek-ai/DeepSeek-R1-0528": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 128000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "DeepSeek R1 reasoning model",
|
||||
},
|
||||
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 430000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
description: "Llama 4 Maverick 17B model",
|
||||
},
|
||||
"Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 106000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "Qwen3 Coder 480B specialized for coding",
|
||||
},
|
||||
"openai/gpt-oss-120b": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 131072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "OpenAI GPT-OSS 120B model",
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
|
@ -32,6 +32,7 @@ import {
|
|||
LiteLLMHandler,
|
||||
ClaudeCodeHandler,
|
||||
SambaNovaHandler,
|
||||
IOIntelligenceHandler,
|
||||
DoubaoHandler,
|
||||
ZAiHandler,
|
||||
FireworksHandler,
|
||||
|
|
@ -137,6 +138,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
return new ZAiHandler(options)
|
||||
case "fireworks":
|
||||
return new FireworksHandler(options)
|
||||
case "io-intelligence":
|
||||
return new IOIntelligenceHandler(options)
|
||||
default:
|
||||
apiProvider satisfies "gemini-cli" | undefined
|
||||
return new AnthropicHandler(options)
|
||||
|
|
|
|||
303
src/api/providers/__tests__/io-intelligence.spec.ts
Normal file
303
src/api/providers/__tests__/io-intelligence.spec.ts
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"
|
||||
import { IOIntelligenceHandler } from "../io-intelligence"
|
||||
import type { ApiHandlerOptions } from "../../../shared/api"
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
const mockCreate = vi.fn()
|
||||
|
||||
// Mock OpenAI
|
||||
vi.mock("openai", () => ({
|
||||
default: class MockOpenAI {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
chat = {
|
||||
completions: {
|
||||
create: vi.fn(),
|
||||
},
|
||||
}
|
||||
constructor(options: any) {
|
||||
this.baseURL = options.baseURL
|
||||
this.apiKey = options.apiKey
|
||||
this.chat.completions.create = mockCreate
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock the fetcher functions
|
||||
vi.mock("../fetchers/io-intelligence", () => ({
|
||||
getIOIntelligenceModels: vi.fn(),
|
||||
getCachedIOIntelligenceModels: vi.fn(() => ({
|
||||
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 430000,
|
||||
description: "Llama 4 Maverick 17B model",
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
"deepseek-ai/DeepSeek-R1-0528": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 128000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "DeepSeek R1 reasoning model",
|
||||
},
|
||||
"Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 106000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "Qwen3 Coder 480B specialized for coding",
|
||||
},
|
||||
"openai/gpt-oss-120b": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 131072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
description: "OpenAI GPT-OSS 120B model",
|
||||
},
|
||||
})),
|
||||
}))
|
||||
|
||||
// Mock constants
|
||||
vi.mock("../constants", () => ({
|
||||
DEFAULT_HEADERS: { "User-Agent": "roo-cline" },
|
||||
}))
|
||||
|
||||
// Mock transform functions
|
||||
vi.mock("../../transform/openai-format", () => ({
|
||||
convertToOpenAiMessages: vi.fn((messages) => messages),
|
||||
}))
|
||||
|
||||
describe("IOIntelligenceHandler", () => {
|
||||
let handler: IOIntelligenceHandler
|
||||
let mockOptions: ApiHandlerOptions
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockOptions = {
|
||||
ioIntelligenceApiKey: "test-api-key",
|
||||
apiModelId: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
modelTemperature: 0.7,
|
||||
includeMaxTokens: false,
|
||||
modelMaxTokens: undefined,
|
||||
} as ApiHandlerOptions
|
||||
|
||||
mockCreate.mockImplementation(async () => ({
|
||||
[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,
|
||||
},
|
||||
}
|
||||
},
|
||||
}))
|
||||
handler = new IOIntelligenceHandler(mockOptions)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it("should create OpenAI client with correct configuration", () => {
|
||||
const ioIntelligenceApiKey = "test-io-intelligence-api-key"
|
||||
const handler = new IOIntelligenceHandler({ ioIntelligenceApiKey })
|
||||
// Verify that the handler was created successfully
|
||||
expect(handler).toBeInstanceOf(IOIntelligenceHandler)
|
||||
expect(handler["client"]).toBeDefined()
|
||||
// Verify the client has the expected properties
|
||||
expect(handler["client"].baseURL).toBe("https://api.intelligence.io.solutions/api/v1")
|
||||
expect(handler["client"].apiKey).toBe(ioIntelligenceApiKey)
|
||||
})
|
||||
|
||||
it("should initialize with correct configuration", () => {
|
||||
expect(handler).toBeInstanceOf(IOIntelligenceHandler)
|
||||
expect(handler["client"]).toBeDefined()
|
||||
expect(handler["options"]).toEqual({
|
||||
...mockOptions,
|
||||
apiKey: mockOptions.ioIntelligenceApiKey,
|
||||
})
|
||||
})
|
||||
|
||||
it("should throw error when API key is missing", () => {
|
||||
const optionsWithoutKey = { ...mockOptions }
|
||||
delete optionsWithoutKey.ioIntelligenceApiKey
|
||||
|
||||
expect(() => new IOIntelligenceHandler(optionsWithoutKey)).toThrow("IO Intelligence API key is required")
|
||||
})
|
||||
|
||||
it("should handle streaming response correctly", async () => {
|
||||
const mockStream = [
|
||||
{
|
||||
choices: [{ delta: { content: "Hello" } }],
|
||||
usage: null,
|
||||
},
|
||||
{
|
||||
choices: [{ delta: { content: " world" } }],
|
||||
usage: null,
|
||||
},
|
||||
{
|
||||
choices: [{ delta: {} }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
||||
},
|
||||
]
|
||||
|
||||
mockCreate.mockResolvedValue({
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
for (const chunk of mockStream) {
|
||||
yield chunk
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
|
||||
|
||||
const stream = handler.createMessage("System prompt", messages)
|
||||
const results = []
|
||||
|
||||
for await (const chunk of stream) {
|
||||
results.push(chunk)
|
||||
}
|
||||
|
||||
expect(results).toHaveLength(3)
|
||||
expect(results[0]).toEqual({ type: "text", text: "Hello" })
|
||||
expect(results[1]).toEqual({ type: "text", text: " world" })
|
||||
expect(results[2]).toEqual({
|
||||
type: "usage",
|
||||
inputTokens: 10,
|
||||
outputTokens: 5,
|
||||
})
|
||||
})
|
||||
|
||||
it("completePrompt method should return text from IO Intelligence API", async () => {
|
||||
const expectedResponse = "This is a test response from IO Intelligence"
|
||||
mockCreate.mockResolvedValueOnce({ choices: [{ message: { content: expectedResponse } }] })
|
||||
const result = await handler.completePrompt("test prompt")
|
||||
expect(result).toBe(expectedResponse)
|
||||
})
|
||||
|
||||
it("should handle errors in completePrompt", async () => {
|
||||
const errorMessage = "IO Intelligence API error"
|
||||
mockCreate.mockRejectedValueOnce(new Error(errorMessage))
|
||||
await expect(handler.completePrompt("test prompt")).rejects.toThrow(
|
||||
`IO Intelligence completion error: ${errorMessage}`,
|
||||
)
|
||||
})
|
||||
|
||||
it("createMessage should yield text content from stream", async () => {
|
||||
const testContent = "This is test content from IO Intelligence stream"
|
||||
|
||||
mockCreate.mockImplementationOnce(() => {
|
||||
return {
|
||||
[Symbol.asyncIterator]: () => ({
|
||||
next: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
done: false,
|
||||
value: { choices: [{ delta: { content: testContent } }] },
|
||||
})
|
||||
.mockResolvedValueOnce({ done: true }),
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
const stream = handler.createMessage("system prompt", [])
|
||||
const firstChunk = await stream.next()
|
||||
|
||||
expect(firstChunk.done).toBe(false)
|
||||
expect(firstChunk.value).toEqual({ type: "text", text: testContent })
|
||||
})
|
||||
|
||||
it("createMessage should yield usage data from stream", async () => {
|
||||
mockCreate.mockImplementationOnce(() => {
|
||||
return {
|
||||
[Symbol.asyncIterator]: () => ({
|
||||
next: vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
done: false,
|
||||
value: { choices: [{ delta: {} }], usage: { prompt_tokens: 10, completion_tokens: 20 } },
|
||||
})
|
||||
.mockResolvedValueOnce({ done: true }),
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
const stream = handler.createMessage("system prompt", [])
|
||||
const firstChunk = await stream.next()
|
||||
|
||||
expect(firstChunk.done).toBe(false)
|
||||
expect(firstChunk.value).toEqual({ type: "usage", inputTokens: 10, outputTokens: 20 })
|
||||
})
|
||||
|
||||
it("should return model info from cache when available", () => {
|
||||
const model = handler.getModel()
|
||||
expect(model.id).toBe("meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8")
|
||||
expect(model.info).toEqual({
|
||||
maxTokens: 8192,
|
||||
contextWindow: 430000,
|
||||
description: "Llama 4 Maverick 17B model",
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
})
|
||||
})
|
||||
|
||||
it("should return fallback model info when not in cache", () => {
|
||||
const handlerWithUnknownModel = new IOIntelligenceHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
})
|
||||
const model = handlerWithUnknownModel.getModel()
|
||||
expect(model.id).toBe("meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8")
|
||||
expect(model.info).toEqual({
|
||||
maxTokens: 8192,
|
||||
contextWindow: 430000,
|
||||
description: "Llama 4 Maverick 17B model",
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
})
|
||||
})
|
||||
|
||||
it("should use default model when no model is specified", () => {
|
||||
const handlerWithoutModel = new IOIntelligenceHandler({
|
||||
...mockOptions,
|
||||
apiModelId: undefined,
|
||||
})
|
||||
const model = handlerWithoutModel.getModel()
|
||||
expect(model.id).toBe("meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8")
|
||||
})
|
||||
|
||||
it("should handle empty response from completePrompt", async () => {
|
||||
mockCreate.mockResolvedValueOnce({
|
||||
choices: [{ message: { content: null } }],
|
||||
})
|
||||
|
||||
const result = await handler.completePrompt("Test prompt")
|
||||
expect(result).toBe("")
|
||||
})
|
||||
|
||||
it("should handle missing choices in completePrompt response", async () => {
|
||||
mockCreate.mockResolvedValueOnce({
|
||||
choices: [],
|
||||
})
|
||||
|
||||
const result = await handler.completePrompt("Test prompt")
|
||||
expect(result).toBe("")
|
||||
})
|
||||
})
|
||||
|
|
@ -24,6 +24,7 @@ vi.mock("../openrouter")
|
|||
vi.mock("../requesty")
|
||||
vi.mock("../glama")
|
||||
vi.mock("../unbound")
|
||||
vi.mock("../io-intelligence")
|
||||
|
||||
// Then imports
|
||||
import type { Mock } from "vitest"
|
||||
|
|
@ -33,15 +34,18 @@ import { getOpenRouterModels } from "../openrouter"
|
|||
import { getRequestyModels } from "../requesty"
|
||||
import { getGlamaModels } from "../glama"
|
||||
import { getUnboundModels } from "../unbound"
|
||||
import { getIOIntelligenceModels } from "../io-intelligence"
|
||||
|
||||
const mockGetLiteLLMModels = getLiteLLMModels as Mock<typeof getLiteLLMModels>
|
||||
const mockGetOpenRouterModels = getOpenRouterModels as Mock<typeof getOpenRouterModels>
|
||||
const mockGetRequestyModels = getRequestyModels as Mock<typeof getRequestyModels>
|
||||
const mockGetGlamaModels = getGlamaModels as Mock<typeof getGlamaModels>
|
||||
const mockGetUnboundModels = getUnboundModels as Mock<typeof getUnboundModels>
|
||||
const mockGetIOIntelligenceModels = getIOIntelligenceModels as Mock<typeof getIOIntelligenceModels>
|
||||
|
||||
const DUMMY_REQUESTY_KEY = "requesty-key-for-testing"
|
||||
const DUMMY_UNBOUND_KEY = "unbound-key-for-testing"
|
||||
const DUMMY_IOINTELLIGENCE_KEY = "io-intelligence-key-for-testing"
|
||||
|
||||
describe("getModels with new GetModelsOptions", () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -137,6 +141,23 @@ describe("getModels with new GetModelsOptions", () => {
|
|||
expect(result).toEqual(mockModels)
|
||||
})
|
||||
|
||||
it("calls IOIntelligenceModels for IO-Intelligence provider", async () => {
|
||||
const mockModels = {
|
||||
"io-intelligence/model": {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
description: "IO Intelligence Model",
|
||||
},
|
||||
}
|
||||
mockGetIOIntelligenceModels.mockResolvedValue(mockModels)
|
||||
|
||||
const result = await getModels({ provider: "io-intelligence", apiKey: DUMMY_IOINTELLIGENCE_KEY })
|
||||
|
||||
expect(mockGetIOIntelligenceModels).toHaveBeenCalled()
|
||||
expect(result).toEqual(mockModels)
|
||||
})
|
||||
|
||||
it("handles errors and re-throws them", async () => {
|
||||
const expectedError = new Error("LiteLLM connection failed")
|
||||
mockGetLiteLLMModels.mockRejectedValue(expectedError)
|
||||
|
|
|
|||
189
src/api/providers/fetchers/io-intelligence.ts
Normal file
189
src/api/providers/fetchers/io-intelligence.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import axios from "axios"
|
||||
import { z } from "zod"
|
||||
import type { ModelInfo } from "@roo-code/types"
|
||||
import { IO_INTELLIGENCE_CACHE_DURATION } from "@roo-code/types"
|
||||
import type { ModelRecord } from "../../../shared/api"
|
||||
|
||||
/**
|
||||
* IO Intelligence Model Schema
|
||||
*/
|
||||
const ioIntelligenceModelSchema = z.object({
|
||||
id: z.string(),
|
||||
object: z.literal("model"),
|
||||
created: z.number(),
|
||||
owned_by: z.string(),
|
||||
root: z.string().nullable().optional(),
|
||||
parent: z.string().nullable().optional(),
|
||||
max_model_len: z.number().nullable().optional(),
|
||||
permission: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
object: z.literal("model_permission"),
|
||||
created: z.number(),
|
||||
allow_create_engine: z.boolean(),
|
||||
allow_sampling: z.boolean(),
|
||||
allow_logprobs: z.boolean(),
|
||||
allow_search_indices: z.boolean(),
|
||||
allow_view: z.boolean(),
|
||||
allow_fine_tuning: z.boolean(),
|
||||
organization: z.string(),
|
||||
group: z.string().nullable(),
|
||||
is_blocking: z.boolean(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
export type IOIntelligenceModel = z.infer<typeof ioIntelligenceModelSchema>
|
||||
|
||||
/**
|
||||
* IO Intelligence API Response Schema
|
||||
*/
|
||||
const ioIntelligenceApiResponseSchema = z.object({
|
||||
object: z.literal("list"),
|
||||
data: z.array(ioIntelligenceModelSchema),
|
||||
})
|
||||
|
||||
type IOIntelligenceApiResponse = z.infer<typeof ioIntelligenceApiResponseSchema>
|
||||
|
||||
/**
|
||||
* Cache entry for storing fetched models
|
||||
*/
|
||||
interface CacheEntry {
|
||||
data: ModelRecord
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
let cache: CacheEntry | null = null
|
||||
|
||||
/**
|
||||
* Model context length mapping based on the documentation
|
||||
* <mcreference link="https://docs.io.net/reference/get-started-with-io-intelligence-api" index="1">1</mcreference>
|
||||
*/
|
||||
const MODEL_CONTEXT_LENGTHS: Record<string, number> = {
|
||||
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": 430000,
|
||||
"deepseek-ai/DeepSeek-R1-0528": 128000,
|
||||
"Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": 106000,
|
||||
"openai/gpt-oss-120b": 131072,
|
||||
}
|
||||
|
||||
/**
|
||||
* Vision models that support images
|
||||
*/
|
||||
const VISION_MODELS = new Set([
|
||||
"Qwen/Qwen2.5-VL-32B-Instruct",
|
||||
"meta-llama/Llama-3.2-90B-Vision-Instruct",
|
||||
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
])
|
||||
|
||||
/**
|
||||
* Parse an IO Intelligence model into ModelInfo format
|
||||
*/
|
||||
function parseIOIntelligenceModel(model: IOIntelligenceModel): ModelInfo {
|
||||
const contextLength = MODEL_CONTEXT_LENGTHS[model.id] || 8192
|
||||
// Cap maxTokens at 32k for very large context windows, or 20% of context length, whichever is smaller
|
||||
const maxTokens = Math.min(contextLength, Math.ceil(contextLength * 0.2), 32768)
|
||||
const supportsImages = VISION_MODELS.has(model.id)
|
||||
|
||||
return {
|
||||
maxTokens,
|
||||
contextWindow: contextLength,
|
||||
supportsImages,
|
||||
supportsPromptCache: false,
|
||||
supportsComputerUse: false,
|
||||
description: `${model.id} via IO Intelligence`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches available models from IO Intelligence
|
||||
* <mcreference link="https://docs.io.net/reference/get-started-with-io-intelligence-api" index="1">1</mcreference>
|
||||
*/
|
||||
export async function getIOIntelligenceModels(apiKey?: string): Promise<ModelRecord> {
|
||||
const now = Date.now()
|
||||
|
||||
// Check cache
|
||||
if (cache && now - cache.timestamp < IO_INTELLIGENCE_CACHE_DURATION) {
|
||||
return cache.data
|
||||
}
|
||||
|
||||
const models: ModelRecord = {}
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
// Add authorization header if API key is provided
|
||||
if (apiKey) {
|
||||
headers.Authorization = `Bearer ${apiKey}`
|
||||
} else {
|
||||
console.error("IO Intelligence API key is required")
|
||||
throw new Error("IO Intelligence API key is required")
|
||||
}
|
||||
|
||||
const response = await axios.get<IOIntelligenceApiResponse>(
|
||||
"https://api.intelligence.io.solutions/api/v1/models",
|
||||
{
|
||||
headers,
|
||||
timeout: 10000, // 10 second timeout
|
||||
},
|
||||
)
|
||||
|
||||
const result = ioIntelligenceApiResponseSchema.safeParse(response.data)
|
||||
|
||||
if (!result.success) {
|
||||
console.error("IO Intelligence models response validation failed:", result.error.format())
|
||||
throw new Error("Invalid response format from IO Intelligence API")
|
||||
}
|
||||
|
||||
for (const model of result.data.data) {
|
||||
models[model.id] = parseIOIntelligenceModel(model)
|
||||
}
|
||||
|
||||
// Update cache
|
||||
cache = {
|
||||
data: models,
|
||||
timestamp: now,
|
||||
}
|
||||
|
||||
return models
|
||||
} catch (error) {
|
||||
console.error("Error fetching IO Intelligence models:", error)
|
||||
|
||||
// Return cached data if available
|
||||
if (cache) {
|
||||
return cache.data
|
||||
}
|
||||
|
||||
// Re-throw with more context
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.response) {
|
||||
throw new Error(
|
||||
`Failed to fetch IO Intelligence models: ${error.response.status} ${error.response.statusText}`,
|
||||
)
|
||||
} else if (error.request) {
|
||||
throw new Error(
|
||||
"Failed to fetch IO Intelligence models: No response from server. Check your internet connection.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Failed to fetch IO Intelligence models: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached models without making an API request
|
||||
*/
|
||||
export function getCachedIOIntelligenceModels(): ModelRecord | null {
|
||||
return cache?.data || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache
|
||||
*/
|
||||
export function clearIOIntelligenceCache(): void {
|
||||
cache = null
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ import { getLiteLLMModels } from "./litellm"
|
|||
import { GetModelsOptions } from "../../../shared/api"
|
||||
import { getOllamaModels } from "./ollama"
|
||||
import { getLMStudioModels } from "./lmstudio"
|
||||
|
||||
import { getIOIntelligenceModels } from "./io-intelligence"
|
||||
const memoryCache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 5 * 60 })
|
||||
|
||||
async function writeModels(router: RouterName, data: ModelRecord) {
|
||||
|
|
@ -78,6 +78,9 @@ export const getModels = async (options: GetModelsOptions): Promise<ModelRecord>
|
|||
case "lmstudio":
|
||||
models = await getLMStudioModels(options.baseUrl)
|
||||
break
|
||||
case "io-intelligence":
|
||||
models = await getIOIntelligenceModels(options.apiKey)
|
||||
break
|
||||
default: {
|
||||
// Ensures router is exhaustively checked if RouterName is a strict union
|
||||
const exhaustiveCheck: never = provider
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export { GlamaHandler } from "./glama"
|
|||
export { GroqHandler } from "./groq"
|
||||
export { HuggingFaceHandler } from "./huggingface"
|
||||
export { HumanRelayHandler } from "./human-relay"
|
||||
export { IOIntelligenceHandler } from "./io-intelligence"
|
||||
export { LiteLLMHandler } from "./lite-llm"
|
||||
export { LmStudioHandler } from "./lm-studio"
|
||||
export { MistralHandler } from "./mistral"
|
||||
|
|
|
|||
42
src/api/providers/io-intelligence.ts
Normal file
42
src/api/providers/io-intelligence.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { ioIntelligenceDefaultModelId, ioIntelligenceModels, type IOIntelligenceModelId } from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../shared/api"
|
||||
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
|
||||
|
||||
export class IOIntelligenceHandler extends BaseOpenAiCompatibleProvider<IOIntelligenceModelId> {
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
if (!options.ioIntelligenceApiKey) {
|
||||
throw new Error("IO Intelligence API key is required")
|
||||
}
|
||||
|
||||
super({
|
||||
...options,
|
||||
providerName: "IO Intelligence",
|
||||
baseURL: "https://api.intelligence.io.solutions/api/v1",
|
||||
defaultProviderModelId: ioIntelligenceDefaultModelId,
|
||||
providerModels: ioIntelligenceModels,
|
||||
defaultTemperature: 0.7,
|
||||
apiKey: options.ioIntelligenceApiKey,
|
||||
})
|
||||
}
|
||||
override getModel() {
|
||||
const modelId = this.options.ioIntelligenceModelId || (ioIntelligenceDefaultModelId as IOIntelligenceModelId)
|
||||
const modelInfo =
|
||||
this.providerModels[modelId as IOIntelligenceModelId] ?? this.providerModels[ioIntelligenceDefaultModelId]
|
||||
|
||||
if (modelInfo) {
|
||||
return { id: modelId as IOIntelligenceModelId, info: modelInfo }
|
||||
}
|
||||
|
||||
// Return the requested model ID even if not found, with fallback info
|
||||
return {
|
||||
id: modelId as IOIntelligenceModelId,
|
||||
info: {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 128000,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -548,6 +548,15 @@ export const webviewMessageHandler = async (
|
|||
{ key: "unbound", options: { provider: "unbound", apiKey: apiConfiguration.unboundApiKey } },
|
||||
]
|
||||
|
||||
// Add IO Intelligence if API key is provided
|
||||
const ioIntelligenceApiKey = apiConfiguration.ioIntelligenceApiKey
|
||||
if (ioIntelligenceApiKey) {
|
||||
modelFetchPromises.push({
|
||||
key: "io-intelligence",
|
||||
options: { provider: "io-intelligence", apiKey: ioIntelligenceApiKey },
|
||||
})
|
||||
}
|
||||
|
||||
// Don't fetch Ollama and LM Studio models by default anymore
|
||||
// They have their own specific handlers: requestOllamaModels and requestLmStudioModels
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ export class ProfileValidator {
|
|||
return profile.ollamaModelId
|
||||
case "requesty":
|
||||
return profile.requestyModelId
|
||||
case "io-intelligence":
|
||||
return profile.ioIntelligenceModelId
|
||||
case "human-relay":
|
||||
case "fake-ai":
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -229,6 +229,22 @@ describe("ProfileValidator", () => {
|
|||
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
||||
})
|
||||
|
||||
// Test for io-intelligence provider which uses ioIntelligenceModelId
|
||||
it(`should extract ioIntelligenceModelId for io-intelligence provider`, () => {
|
||||
const allowList: OrganizationAllowList = {
|
||||
allowAll: false,
|
||||
providers: {
|
||||
"io-intelligence": { allowAll: false, models: ["test-model"] },
|
||||
},
|
||||
}
|
||||
const profile: ProviderSettings = {
|
||||
apiProvider: "io-intelligence" as any,
|
||||
ioIntelligenceModelId: "test-model",
|
||||
}
|
||||
|
||||
expect(ProfileValidator.isProfileAllowed(profile, allowList)).toBe(true)
|
||||
})
|
||||
|
||||
it("should extract vsCodeLmModelSelector.id for vscode-lm provider", () => {
|
||||
const allowList: OrganizationAllowList = {
|
||||
allowAll: false,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,16 @@ export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider"> & {
|
|||
|
||||
// RouterName
|
||||
|
||||
const routerNames = ["openrouter", "requesty", "glama", "unbound", "litellm", "ollama", "lmstudio"] as const
|
||||
const routerNames = [
|
||||
"openrouter",
|
||||
"requesty",
|
||||
"glama",
|
||||
"unbound",
|
||||
"litellm",
|
||||
"ollama",
|
||||
"lmstudio",
|
||||
"io-intelligence",
|
||||
] as const
|
||||
|
||||
export type RouterName = (typeof routerNames)[number]
|
||||
|
||||
|
|
@ -121,3 +130,4 @@ export type GetModelsOptions =
|
|||
| { provider: "litellm"; apiKey: string; baseUrl: string }
|
||||
| { provider: "ollama"; baseUrl?: string }
|
||||
| { provider: "lmstudio"; baseUrl?: string }
|
||||
| { provider: "io-intelligence"; apiKey: string }
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
internationalZAiDefaultModelId,
|
||||
mainlandZAiDefaultModelId,
|
||||
fireworksDefaultModelId,
|
||||
ioIntelligenceDefaultModelId,
|
||||
} from "@roo-code/types"
|
||||
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
|
|
@ -68,6 +69,7 @@ import {
|
|||
Glama,
|
||||
Groq,
|
||||
HuggingFace,
|
||||
IOIntelligence,
|
||||
LMStudio,
|
||||
LiteLLM,
|
||||
Mistral,
|
||||
|
|
@ -320,6 +322,7 @@ const ApiOptions = ({
|
|||
: internationalZAiDefaultModelId,
|
||||
},
|
||||
fireworks: { field: "apiModelId", default: fireworksDefaultModelId },
|
||||
"io-intelligence": { field: "ioIntelligenceModelId", default: ioIntelligenceDefaultModelId },
|
||||
openai: { field: "openAiModelId" },
|
||||
ollama: { field: "ollamaModelId" },
|
||||
lmstudio: { field: "lmStudioModelId" },
|
||||
|
|
@ -548,6 +551,15 @@ const ApiOptions = ({
|
|||
<ZAi apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "io-intelligence" && (
|
||||
<IOIntelligence
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
organizationAllowList={organizationAllowList}
|
||||
modelValidationError={modelValidationError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "human-relay" && (
|
||||
<>
|
||||
<div className="text-sm text-vscode-descriptionForeground">
|
||||
|
|
|
|||
|
|
@ -28,7 +28,13 @@ import { ApiErrorMessage } from "./ApiErrorMessage"
|
|||
|
||||
type ModelIdKey = keyof Pick<
|
||||
ProviderSettings,
|
||||
"glamaModelId" | "openRouterModelId" | "unboundModelId" | "requestyModelId" | "openAiModelId" | "litellmModelId"
|
||||
| "glamaModelId"
|
||||
| "openRouterModelId"
|
||||
| "unboundModelId"
|
||||
| "requestyModelId"
|
||||
| "openAiModelId"
|
||||
| "litellmModelId"
|
||||
| "ioIntelligenceModelId"
|
||||
>
|
||||
|
||||
interface ModelPickerProps {
|
||||
|
|
|
|||
|
|
@ -69,4 +69,5 @@ export const PROVIDERS = [
|
|||
{ value: "sambanova", label: "SambaNova" },
|
||||
{ value: "zai", label: "Z AI" },
|
||||
{ value: "fireworks", label: "Fireworks AI" },
|
||||
{ value: "io-intelligence", label: "IO Intelligence" },
|
||||
].sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import { useCallback } from "react"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import type { ProviderSettings, OrganizationAllowList } from "@roo-code/types"
|
||||
import { ioIntelligenceDefaultModelId, ioIntelligenceModels } from "@roo-code/types"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
|
||||
import { useExtensionState } from "@src/context/ExtensionStateContext"
|
||||
import { ModelPicker } from "../ModelPicker"
|
||||
|
||||
import { inputEventTransform } from "../transforms"
|
||||
|
||||
type IOIntelligenceProps = {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
organizationAllowList: OrganizationAllowList
|
||||
modelValidationError?: string
|
||||
}
|
||||
|
||||
export const IOIntelligence = ({
|
||||
apiConfiguration,
|
||||
setApiConfigurationField,
|
||||
organizationAllowList,
|
||||
modelValidationError,
|
||||
}: IOIntelligenceProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
const { routerModels } = useExtensionState()
|
||||
|
||||
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?.ioIntelligenceApiKey || ""}
|
||||
type="password"
|
||||
onInput={handleInputChange("ioIntelligenceApiKey")}
|
||||
placeholder={t("settings:providers.ioIntelligenceApiKeyPlaceholder")}
|
||||
className="w-full">
|
||||
<label className="block font-medium mb-1">{t("settings:providers.ioIntelligenceApiKey")}</label>
|
||||
</VSCodeTextField>
|
||||
<div className="text-sm text-vscode-descriptionForeground -mt-2">
|
||||
{t("settings:providers.apiKeyStorageNotice")}
|
||||
</div>
|
||||
{!apiConfiguration?.ioIntelligenceApiKey && (
|
||||
<VSCodeButtonLink href="https://ai.io.net/ai/api-keys" appearance="secondary">
|
||||
{t("settings:providers.getIoIntelligenceApiKey")}
|
||||
</VSCodeButtonLink>
|
||||
)}
|
||||
<ModelPicker
|
||||
apiConfiguration={apiConfiguration}
|
||||
defaultModelId={ioIntelligenceDefaultModelId}
|
||||
models={routerModels?.["io-intelligence"] ?? ioIntelligenceModels}
|
||||
modelIdKey="ioIntelligenceModelId"
|
||||
serviceName="IO Intelligence"
|
||||
serviceUrl="https://api.intelligence.io.solutions/api/v1/models"
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
organizationAllowList={organizationAllowList}
|
||||
errorMessage={modelValidationError}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ export { Gemini } from "./Gemini"
|
|||
export { Glama } from "./Glama"
|
||||
export { Groq } from "./Groq"
|
||||
export { HuggingFace } from "./HuggingFace"
|
||||
export { IOIntelligence } from "./IOIntelligence"
|
||||
export { LMStudio } from "./LMStudio"
|
||||
export { Mistral } from "./Mistral"
|
||||
export { Moonshot } from "./Moonshot"
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -110,6 +111,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -164,6 +166,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -219,6 +222,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -263,6 +267,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -310,7 +315,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: {}, glama: {}, unbound: {}, litellm: {}, "io-intelligence": {} },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
} as any)
|
||||
|
|
@ -379,6 +384,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
@ -417,6 +423,7 @@ describe("useSelectedModel", () => {
|
|||
glama: {},
|
||||
unbound: {},
|
||||
litellm: {},
|
||||
"io-intelligence": {},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ import {
|
|||
mainlandZAiModels,
|
||||
fireworksModels,
|
||||
fireworksDefaultModelId,
|
||||
ioIntelligenceDefaultModelId,
|
||||
ioIntelligenceModels,
|
||||
} from "@roo-code/types"
|
||||
|
||||
import type { ModelRecord, RouterModels } from "@roo/api"
|
||||
|
|
@ -277,6 +279,12 @@ function getSelectedModel({
|
|||
const info = fireworksModels[id as keyof typeof fireworksModels]
|
||||
return { id, info }
|
||||
}
|
||||
case "io-intelligence": {
|
||||
const id = apiConfiguration.ioIntelligenceModelId ?? ioIntelligenceDefaultModelId
|
||||
const info =
|
||||
routerModels["io-intelligence"]?.[id] ?? ioIntelligenceModels[id as keyof typeof ioIntelligenceModels]
|
||||
return { id, info }
|
||||
}
|
||||
// case "anthropic":
|
||||
// case "human-relay":
|
||||
// case "fake-ai":
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ca/settings.json
generated
3
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Obtenir clau API de Chutes",
|
||||
"fireworksApiKey": "Clau API de Fireworks",
|
||||
"getFireworksApiKey": "Obtenir clau API de Fireworks",
|
||||
"ioIntelligenceApiKey": "Clau API d'IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Introdueix la teva clau d'API de IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Obtenir clau API d'IO Intelligence",
|
||||
"deepSeekApiKey": "Clau API de DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtenir clau API de DeepSeek",
|
||||
"doubaoApiKey": "Clau API de Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/settings.json
generated
3
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -265,6 +265,9 @@
|
|||
"getChutesApiKey": "Chutes API-Schlüssel erhalten",
|
||||
"fireworksApiKey": "Fireworks API-Schlüssel",
|
||||
"getFireworksApiKey": "Fireworks API-Schlüssel erhalten",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API-Schlüssel",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Gib deinen IO Intelligence API-Schlüssel ein",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence API-Schlüssel erhalten",
|
||||
"deepSeekApiKey": "DeepSeek API-Schlüssel",
|
||||
"getDeepSeekApiKey": "DeepSeek API-Schlüssel erhalten",
|
||||
"moonshotApiKey": "Moonshot API-Schlüssel",
|
||||
|
|
|
|||
|
|
@ -262,6 +262,9 @@
|
|||
"getChutesApiKey": "Get Chutes API Key",
|
||||
"fireworksApiKey": "Fireworks API Key",
|
||||
"getFireworksApiKey": "Get Fireworks API Key",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API Key",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Enter your IO Intelligence API key",
|
||||
"getIoIntelligenceApiKey": "Get IO Intelligence API Key",
|
||||
"deepSeekApiKey": "DeepSeek API Key",
|
||||
"getDeepSeekApiKey": "Get DeepSeek API Key",
|
||||
"doubaoApiKey": "Doubao API Key",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/settings.json
generated
3
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Obtener clave API de Chutes",
|
||||
"fireworksApiKey": "Clave API de Fireworks",
|
||||
"getFireworksApiKey": "Obtener clave API de Fireworks",
|
||||
"ioIntelligenceApiKey": "Clave API de IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Introduce tu clave de API de IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Obtener clave API de IO Intelligence",
|
||||
"deepSeekApiKey": "Clave API de DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtener clave API de DeepSeek",
|
||||
"doubaoApiKey": "Clave API de Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/settings.json
generated
3
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Obtenir la clé API Chutes",
|
||||
"fireworksApiKey": "Clé API Fireworks",
|
||||
"getFireworksApiKey": "Obtenir la clé API Fireworks",
|
||||
"ioIntelligenceApiKey": "Clé API IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Saisissez votre clé d'API IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Obtenir la clé API IO Intelligence",
|
||||
"deepSeekApiKey": "Clé API DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtenir la clé API DeepSeek",
|
||||
"doubaoApiKey": "Clé API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/settings.json
generated
3
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Chutes API कुंजी प्राप्त करें",
|
||||
"fireworksApiKey": "Fireworks API कुंजी",
|
||||
"getFireworksApiKey": "Fireworks API कुंजी प्राप्त करें",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API कुंजी",
|
||||
"ioIntelligenceApiKeyPlaceholder": "अपना आईओ इंटेलिजेंस एपीआई कुंजी दर्ज करें",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence API कुंजी प्राप्त करें",
|
||||
"deepSeekApiKey": "DeepSeek API कुंजी",
|
||||
"getDeepSeekApiKey": "DeepSeek API कुंजी प्राप्त करें",
|
||||
"doubaoApiKey": "डौबाओ API कुंजी",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/settings.json
generated
3
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -267,6 +267,9 @@
|
|||
"getChutesApiKey": "Dapatkan Chutes API Key",
|
||||
"fireworksApiKey": "Fireworks API Key",
|
||||
"getFireworksApiKey": "Dapatkan Fireworks API Key",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API Key",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Masukkan kunci API IO Intelligence Anda",
|
||||
"getIoIntelligenceApiKey": "Dapatkan IO Intelligence API Key",
|
||||
"deepSeekApiKey": "DeepSeek API Key",
|
||||
"getDeepSeekApiKey": "Dapatkan DeepSeek API Key",
|
||||
"doubaoApiKey": "Kunci API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/settings.json
generated
3
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Ottieni chiave API Chutes",
|
||||
"fireworksApiKey": "Chiave API Fireworks",
|
||||
"getFireworksApiKey": "Ottieni chiave API Fireworks",
|
||||
"ioIntelligenceApiKey": "Chiave API IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Inserisci la tua chiave API IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Ottieni chiave API IO Intelligence",
|
||||
"deepSeekApiKey": "Chiave API DeepSeek",
|
||||
"getDeepSeekApiKey": "Ottieni chiave API DeepSeek",
|
||||
"doubaoApiKey": "Chiave API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/settings.json
generated
3
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Chutes APIキーを取得",
|
||||
"fireworksApiKey": "Fireworks APIキー",
|
||||
"getFireworksApiKey": "Fireworks APIキーを取得",
|
||||
"ioIntelligenceApiKey": "IO Intelligence APIキー",
|
||||
"ioIntelligenceApiKeyPlaceholder": "IO Intelligence APIキーを入力してください",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence APIキーを取得",
|
||||
"deepSeekApiKey": "DeepSeek APIキー",
|
||||
"getDeepSeekApiKey": "DeepSeek APIキーを取得",
|
||||
"doubaoApiKey": "Doubao APIキー",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/settings.json
generated
3
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Chutes API 키 받기",
|
||||
"fireworksApiKey": "Fireworks API 키",
|
||||
"getFireworksApiKey": "Fireworks API 키 받기",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API 키",
|
||||
"ioIntelligenceApiKeyPlaceholder": "IO Intelligence API 키를 입력하세요",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence API 키 받기",
|
||||
"deepSeekApiKey": "DeepSeek API 키",
|
||||
"getDeepSeekApiKey": "DeepSeek API 키 받기",
|
||||
"doubaoApiKey": "Doubao API 키",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/settings.json
generated
3
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Chutes API-sleutel ophalen",
|
||||
"fireworksApiKey": "Fireworks API-sleutel",
|
||||
"getFireworksApiKey": "Fireworks API-sleutel ophalen",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API-sleutel",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Voer je IO Intelligence API-sleutel in",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence API-sleutel ophalen",
|
||||
"deepSeekApiKey": "DeepSeek API-sleutel",
|
||||
"getDeepSeekApiKey": "DeepSeek API-sleutel ophalen",
|
||||
"doubaoApiKey": "Doubao API-sleutel",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/settings.json
generated
3
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Uzyskaj klucz API Chutes",
|
||||
"fireworksApiKey": "Klucz API Fireworks",
|
||||
"getFireworksApiKey": "Uzyskaj klucz API Fireworks",
|
||||
"ioIntelligenceApiKey": "Klucz API IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Wprowadź swój klucz API IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Uzyskaj klucz API IO Intelligence",
|
||||
"deepSeekApiKey": "Klucz API DeepSeek",
|
||||
"getDeepSeekApiKey": "Uzyskaj klucz API DeepSeek",
|
||||
"doubaoApiKey": "Klucz API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
3
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Obter chave de API Chutes",
|
||||
"fireworksApiKey": "Chave de API Fireworks",
|
||||
"getFireworksApiKey": "Obter chave de API Fireworks",
|
||||
"ioIntelligenceApiKey": "Chave de API IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Insira sua chave de API da IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Obter chave de API IO Intelligence",
|
||||
"deepSeekApiKey": "Chave de API DeepSeek",
|
||||
"getDeepSeekApiKey": "Obter chave de API DeepSeek",
|
||||
"doubaoApiKey": "Chave de API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/settings.json
generated
3
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Получить Chutes API-ключ",
|
||||
"fireworksApiKey": "Fireworks API-ключ",
|
||||
"getFireworksApiKey": "Получить Fireworks API-ключ",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API-ключ",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Введите свой ключ API IO Intelligence",
|
||||
"getIoIntelligenceApiKey": "Получить IO Intelligence API-ключ",
|
||||
"deepSeekApiKey": "DeepSeek API-ключ",
|
||||
"getDeepSeekApiKey": "Получить DeepSeek API-ключ",
|
||||
"doubaoApiKey": "Doubao API-ключ",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/settings.json
generated
3
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Chutes API Anahtarı Al",
|
||||
"fireworksApiKey": "Fireworks API Anahtarı",
|
||||
"getFireworksApiKey": "Fireworks API Anahtarı Al",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API Anahtarı",
|
||||
"ioIntelligenceApiKeyPlaceholder": "IO Intelligence API anahtarınızı girin",
|
||||
"getIoIntelligenceApiKey": "IO Intelligence API Anahtarı Al",
|
||||
"deepSeekApiKey": "DeepSeek API Anahtarı",
|
||||
"getDeepSeekApiKey": "DeepSeek API Anahtarı Al",
|
||||
"doubaoApiKey": "Doubao API Anahtarı",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/settings.json
generated
3
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "Lấy khóa API Chutes",
|
||||
"fireworksApiKey": "Khóa API Fireworks",
|
||||
"getFireworksApiKey": "Lấy khóa API Fireworks",
|
||||
"ioIntelligenceApiKey": "Khóa API IO Intelligence",
|
||||
"ioIntelligenceApiKeyPlaceholder": "Nhập khóa API IO Intelligence của bạn",
|
||||
"getIoIntelligenceApiKey": "Lấy khóa API IO Intelligence",
|
||||
"deepSeekApiKey": "Khóa API DeepSeek",
|
||||
"getDeepSeekApiKey": "Lấy khóa API DeepSeek",
|
||||
"doubaoApiKey": "Khóa API Doubao",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
3
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "获取 Chutes API 密钥",
|
||||
"fireworksApiKey": "Fireworks API 密钥",
|
||||
"getFireworksApiKey": "获取 Fireworks API 密钥",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API 密钥",
|
||||
"ioIntelligenceApiKeyPlaceholder": "输入您的 IO Intelligence API 密钥",
|
||||
"getIoIntelligenceApiKey": "获取 IO Intelligence API 密钥",
|
||||
"deepSeekApiKey": "DeepSeek API 密钥",
|
||||
"getDeepSeekApiKey": "获取 DeepSeek API 密钥",
|
||||
"doubaoApiKey": "豆包 API 密钥",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
3
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -263,6 +263,9 @@
|
|||
"getChutesApiKey": "取得 Chutes API 金鑰",
|
||||
"fireworksApiKey": "Fireworks API 金鑰",
|
||||
"getFireworksApiKey": "取得 Fireworks API 金鑰",
|
||||
"ioIntelligenceApiKey": "IO Intelligence API 金鑰",
|
||||
"ioIntelligenceApiKeyPlaceholder": "輸入您的 IO Intelligence API 金鑰",
|
||||
"getIoIntelligenceApiKey": "取得 IO Intelligence API 金鑰",
|
||||
"deepSeekApiKey": "DeepSeek API 金鑰",
|
||||
"getDeepSeekApiKey": "取得 DeepSeek API 金鑰",
|
||||
"doubaoApiKey": "豆包 API 金鑰",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ describe("Model Validation Functions", () => {
|
|||
litellm: {},
|
||||
ollama: {},
|
||||
lmstudio: {},
|
||||
"io-intelligence": {},
|
||||
}
|
||||
|
||||
const allowAllOrganization: OrganizationAllowList = {
|
||||
|
|
@ -185,5 +186,25 @@ describe("Model Validation Functions", () => {
|
|||
)
|
||||
expect(result).toBeUndefined() // Should exclude model-specific org errors
|
||||
})
|
||||
|
||||
it("returns undefined for valid IO Intelligence model", () => {
|
||||
const config: ProviderSettings = {
|
||||
apiProvider: "io-intelligence",
|
||||
glamaModelId: "valid-model",
|
||||
}
|
||||
|
||||
const result = getModelValidationError(config, mockRouterModels, allowAllOrganization)
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns error for invalid IO Intelligence model", () => {
|
||||
const config: ProviderSettings = {
|
||||
apiProvider: "io-intelligence",
|
||||
glamaModelId: "invalid-model",
|
||||
}
|
||||
|
||||
const result = getModelValidationError(config, mockRouterModels, allowAllOrganization)
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -120,6 +120,11 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
|
|||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
case "io-intelligence":
|
||||
if (!apiConfiguration.ioIntelligenceApiKey) {
|
||||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
|
@ -186,6 +191,8 @@ function getModelIdForProvider(apiConfiguration: ProviderSettings, provider: str
|
|||
return apiConfiguration.vsCodeLmModelSelector?.id
|
||||
case "huggingface":
|
||||
return apiConfiguration.huggingFaceModelId
|
||||
case "io-intelligence":
|
||||
return apiConfiguration.ioIntelligenceModelId
|
||||
default:
|
||||
return apiConfiguration.apiModelId
|
||||
}
|
||||
|
|
@ -256,6 +263,9 @@ export function validateModelId(apiConfiguration: ProviderSettings, routerModels
|
|||
case "litellm":
|
||||
modelId = apiConfiguration.litellmModelId
|
||||
break
|
||||
case "io-intelligence":
|
||||
modelId = apiConfiguration.ioIntelligenceModelId
|
||||
break
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue