Skip to content

Commit 589841c

Browse files
committed
feat(io-intelligence): make api key optional and improve model handling
- Make IO Intelligence API key optional for models endpoint - Add model fetching with fallback to defaults - Enhance model info parsing with additional fields from API - Remove hardcoded model properties in favor of API-provided data
1 parent 9f41ee0 commit 589841c

File tree

4 files changed

+68
-45
lines changed

4 files changed

+68
-45
lines changed

src/api/providers/fetchers/io-intelligence.ts

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import axios from "axios"
22
import { z } from "zod"
3-
43
import { type ModelInfo, IO_INTELLIGENCE_CACHE_DURATION } from "@roo-code/types"
5-
64
import type { ModelRecord } from "../../../shared/api"
5+
import { parseApiPrice } from "../../../shared/cost"
76

87
const ioIntelligenceModelSchema = z.object({
98
id: z.string(),
@@ -29,6 +28,15 @@ const ioIntelligenceModelSchema = z.object({
2928
is_blocking: z.boolean(),
3029
}),
3130
),
31+
max_tokens: z.number().nullable().optional(),
32+
context_window: z.number().optional(),
33+
supports_images_input: z.boolean().optional().default(false),
34+
supports_prompt_cache: z.boolean().optional().default(false),
35+
input_token_price: z.number().nullable().optional(),
36+
output_token_price: z.number().nullable().optional(),
37+
cache_write_token_price: z.number().nullable().optional(),
38+
cache_read_token_price: z.number().nullable().optional(),
39+
precision: z.string().nullable().optional(),
3240
})
3341

3442
export type IOIntelligenceModel = z.infer<typeof ioIntelligenceModelSchema>
@@ -47,35 +55,22 @@ interface CacheEntry {
4755

4856
let cache: CacheEntry | null = null
4957

50-
/**
51-
* Model context length mapping based on the documentation
52-
* <mcreference link="https://docs.io.net/reference/get-started-with-io-intelligence-api" index="1">1</mcreference>
53-
*/
54-
const MODEL_CONTEXT_LENGTHS: Record<string, number> = {
55-
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8": 430000,
56-
"deepseek-ai/DeepSeek-R1-0528": 128000,
57-
"Intel/Qwen3-Coder-480B-A35B-Instruct-int4-mixed-ar": 106000,
58-
"openai/gpt-oss-120b": 131072,
59-
}
60-
61-
const VISION_MODELS = new Set([
62-
"Qwen/Qwen2.5-VL-32B-Instruct",
63-
"meta-llama/Llama-3.2-90B-Vision-Instruct",
64-
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
65-
])
66-
6758
function parseIOIntelligenceModel(model: IOIntelligenceModel): ModelInfo {
68-
const contextLength = MODEL_CONTEXT_LENGTHS[model.id] || 8192
69-
// Cap maxTokens at 32k for very large context windows, or 20% of context length, whichever is smaller.
70-
const maxTokens = Math.min(contextLength, Math.ceil(contextLength * 0.2), 32768)
71-
const supportsImages = VISION_MODELS.has(model.id)
59+
const contextWindow = model.context_window || 8192
60+
61+
// Use API max_tokens if provided, otherwise calculate 20% of context window
62+
const maxTokens = model.max_tokens && model.max_tokens > 0 ? model.max_tokens : Math.ceil(contextWindow * 0.2)
7263

7364
return {
7465
maxTokens,
75-
contextWindow: contextLength,
76-
supportsImages,
77-
supportsPromptCache: false,
78-
supportsComputerUse: false,
66+
contextWindow,
67+
supportsImages: model.supports_images_input,
68+
supportsPromptCache: model.supports_prompt_cache,
69+
supportsComputerUse: false, // Not supported by IO Intelligence
70+
inputPrice: parseApiPrice(model.input_token_price),
71+
outputPrice: parseApiPrice(model.output_token_price),
72+
cacheWritesPrice: parseApiPrice(model.cache_write_token_price),
73+
cacheReadsPrice: parseApiPrice(model.cache_read_token_price),
7974
description: `${model.id} via IO Intelligence`,
8075
}
8176
}
@@ -98,18 +93,17 @@ export async function getIOIntelligenceModels(apiKey?: string): Promise<ModelRec
9893
"Content-Type": "application/json",
9994
}
10095

96+
// Note: IO Intelligence models endpoint does not require authentication
97+
// API key is optional for future use if needed
10198
if (apiKey) {
10299
headers.Authorization = `Bearer ${apiKey}`
103-
} else {
104-
console.error("IO Intelligence API key is required")
105-
throw new Error("IO Intelligence API key is required")
106100
}
107101

108102
const response = await axios.get<IOIntelligenceApiResponse>(
109103
"https://api.intelligence.io.solutions/api/v1/models",
110104
{
111105
headers,
112-
timeout: 10_000,
106+
timeout: 10000,
113107
},
114108
)
115109

src/api/providers/io-intelligence.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { ioIntelligenceDefaultModelId, ioIntelligenceModels, type IOIntelligenceModelId } from "@roo-code/types"
2+
import { Anthropic } from "@anthropic-ai/sdk"
23

3-
import type { ApiHandlerOptions } from "../../shared/api"
4+
import type { ApiHandlerOptions, ModelRecord } from "../../shared/api"
45
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
6+
import { getModels } from "./fetchers/modelCache"
7+
import type { ApiHandlerCreateMessageMetadata } from "../index"
8+
import { ApiStream } from "../transform/stream"
59

610
export class IOIntelligenceHandler extends BaseOpenAiCompatibleProvider<IOIntelligenceModelId> {
11+
protected models: ModelRecord = {}
12+
713
constructor(options: ApiHandlerOptions) {
814
if (!options.ioIntelligenceApiKey) {
915
throw new Error("IO Intelligence API key is required")
@@ -20,11 +26,38 @@ export class IOIntelligenceHandler extends BaseOpenAiCompatibleProvider<IOIntell
2026
})
2127
}
2228

29+
public async fetchModel() {
30+
try {
31+
this.models = await getModels({
32+
provider: "io-intelligence",
33+
apiKey: this.options.ioIntelligenceApiKey || undefined,
34+
})
35+
} catch (error) {
36+
console.error("Failed to fetch IO Intelligence models, falling back to default models:", error)
37+
this.models = ioIntelligenceModels
38+
}
39+
return this.getModel()
40+
}
41+
42+
override async *createMessage(
43+
systemPrompt: string,
44+
messages: Anthropic.Messages.MessageParam[],
45+
metadata?: ApiHandlerCreateMessageMetadata,
46+
): ApiStream {
47+
await this.fetchModel()
48+
49+
yield* super.createMessage(systemPrompt, messages, metadata)
50+
}
51+
2352
override getModel() {
24-
const modelId = this.options.ioIntelligenceModelId || (ioIntelligenceDefaultModelId as IOIntelligenceModelId)
53+
const modelId = this.options.ioIntelligenceModelId || ioIntelligenceDefaultModelId
54+
let modelInfo = this.models[modelId]
2555

26-
const modelInfo =
27-
this.providerModels[modelId as IOIntelligenceModelId] ?? this.providerModels[ioIntelligenceDefaultModelId]
56+
if (!modelInfo) {
57+
modelInfo =
58+
this.providerModels[modelId as IOIntelligenceModelId] ??
59+
this.providerModels[ioIntelligenceDefaultModelId]
60+
}
2861

2962
if (modelInfo) {
3063
return { id: modelId as IOIntelligenceModelId, info: modelInfo }

src/core/webview/webviewMessageHandler.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -806,15 +806,11 @@ export const webviewMessageHandler = async (
806806
},
807807
]
808808

809-
// Add IO Intelligence if API key is provided.
810-
const ioIntelligenceApiKey = apiConfiguration.ioIntelligenceApiKey
811-
812-
if (ioIntelligenceApiKey) {
813-
modelFetchPromises.push({
814-
key: "io-intelligence",
815-
options: { provider: "io-intelligence", apiKey: ioIntelligenceApiKey },
816-
})
817-
}
809+
// Add IO Intelligence (models endpoint doesn't require API key)
810+
modelFetchPromises.push({
811+
key: "io-intelligence",
812+
options: { provider: "io-intelligence", apiKey: apiConfiguration.ioIntelligenceApiKey },
813+
})
818814

819815
// Don't fetch Ollama and LM Studio models by default anymore.
820816
// They have their own specific handlers: requestOllamaModels and requestLmStudioModels.

src/shared/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ const dynamicProviderExtras = {
157157
huggingface: {} as {}, // eslint-disable-line @typescript-eslint/no-empty-object-type
158158
litellm: {} as { apiKey: string; baseUrl: string },
159159
deepinfra: {} as { apiKey?: string; baseUrl?: string },
160-
"io-intelligence": {} as { apiKey: string },
160+
"io-intelligence": {} as { apiKey?: string },
161161
requesty: {} as { apiKey?: string; baseUrl?: string },
162162
unbound: {} as { apiKey?: string },
163163
glama: {} as {}, // eslint-disable-line @typescript-eslint/no-empty-object-type

0 commit comments

Comments
 (0)