diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index b75ca21238..037ac196c9 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -101,52 +101,33 @@ type OpenRouterModelEndpointsResponse = z.infer }, // kilocode_change: added headers ): Promise> { + // Return static models instead of making API calls const models: Record = {} - const baseURL = "https://api.matterai.so/v1/web" - try { - // kilocode_change: use axios with timeout instead of fetch - const response = await axios.get(`${baseURL}/models`, { - headers: { ...DEFAULT_HEADERS, ...(options?.headers ?? {}) }, - timeout: 120000, // 60 seconds timeout + // Import the static models from the shared file + const { KILO_CODE_MODELS } = await import("../kilocode-models") + + for (const [id, model] of Object.entries(KILO_CODE_MODELS)) { + models[id] = parseOpenRouterModel({ + id, + model: { + name: model.name, + description: model.description, + context_length: model.context_length, + max_completion_tokens: model.max_output_length, + pricing: { + prompt: model.pricing.prompt, + completion: model.pricing.completion, + input_cache_write: model.pricing.input_cache_writes, + input_cache_read: model.pricing.input_cache_reads, + }, + }, + displayName: model.name, + inputModality: model.input_modalities, + outputModality: model.output_modalities, + maxTokens: model.max_output_length, + supportedParameters: model.supported_sampling_parameters, }) - - const json = response.data - const result = openRouterModelsResponseSchema.safeParse(json) - const data = result.success ? result.data.data : json.data - // kilocode_change end - - if (!result.success) { - // kilocode_change start - throw new Error( - "OpenRouter models response is invalid: " + JSON.stringify(result.error.format(), undefined, 2), - ) - // kilocode_change end - } - - for (const model of data) { - const { id, architecture, top_provider, supported_parameters = [] } = model - - // Skip image generation models (models that output images) - if (architecture?.output_modalities?.includes("image")) { - continue - } - - models[id] = parseOpenRouterModel({ - id, - model, - displayName: model.name, // kilocode_change - inputModality: architecture?.input_modalities, - outputModality: architecture?.output_modalities, - maxTokens: top_provider?.max_completion_tokens, - supportedParameters: supported_parameters, - }) - } - } catch (error) { - console.error( - `Error fetching OpenRouter models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, - ) - throw error // kilocode_change } return models @@ -160,44 +141,38 @@ export async function getOpenRouterModelEndpoints( modelId: string, options?: ApiHandlerOptions, ): Promise> { + // Return static models instead of making API calls const models: Record = {} - const baseURL = "https://api.matterai.so/v1/web" - try { - console.log("getOpenRouterModelEndpoints 1", baseURL, modelId) - const response = await axios.get(`${baseURL}/models/${modelId}`, { - timeout: 120000, // 60 seconds timeout - }) - const result = openRouterModelEndpointsResponseSchema.safeParse(response.data) - const data = result.success ? result.data.data : response.data.data - - if (!result.success) { - console.error("OpenRouter model endpoints response is invalid", result.error.format()) - } - - const { id, architecture, endpoints } = data - - // Skip image generation models (models that output images) - if (architecture?.output_modalities?.includes("image")) { - return models - } - - for (const endpoint of endpoints) { - models[endpoint.tag ?? endpoint.provider_name] = parseOpenRouterModel({ - id, - model: endpoint, - displayName: endpoint.model_name, // kilocode_change - inputModality: architecture?.input_modalities, - outputModality: architecture?.output_modalities, - maxTokens: endpoint.max_completion_tokens, - }) - } - } catch (error) { - console.error( - `Error fetching OpenRouter model endpoints: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`, - ) + // Import the static models from the shared file + const { KILO_CODE_MODELS } = await import("../kilocode-models") + + const model = KILO_CODE_MODELS[modelId] + if (!model) { + return models } + models["KiloCode"] = parseOpenRouterModel({ + id: model.id, + model: { + name: model.name, + description: model.description, + context_length: model.context_length, + max_completion_tokens: model.max_output_length, + pricing: { + prompt: model.pricing.prompt, + completion: model.pricing.completion, + input_cache_write: model.pricing.input_cache_writes, + input_cache_read: model.pricing.input_cache_reads, + }, + }, + displayName: model.name, + inputModality: model.input_modalities, + outputModality: model.output_modalities, + maxTokens: model.max_output_length, + supportedParameters: model.supported_sampling_parameters, + }) + return models } diff --git a/src/api/providers/kilocode-models.ts b/src/api/providers/kilocode-models.ts new file mode 100644 index 0000000000..bed3bf0d04 --- /dev/null +++ b/src/api/providers/kilocode-models.ts @@ -0,0 +1,133 @@ +import type { ModelParameter } from "@roo-code/types" + +export type KiloCodeModel = { + id: string + name: string + description: string + input_modalities: string[] + context_length: number + max_output_length: number + output_modalities: string[] + supported_sampling_parameters: string[] + supported_features: string[] + openrouter: { + slug: string + } + datacenters: Array<{ country_code: string }> + created: number + owned_by: string + pricing: { + prompt?: string + completion?: string + image?: string + request?: string + input_cache_reads?: string + input_cache_writes?: string + } +} + +export const KILO_CODE_MODELS: Record = { + "axon-code": { + id: "axon-code", + name: "Axon Code", + description: "Axon Code is super intelligent LLM model for coding tasks", + input_modalities: ["text"], + context_length: 256000, + max_output_length: 32768, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon", + }, + datacenters: [{ country_code: "US" }], + created: 1750426201, + owned_by: "matterai", + pricing: { + prompt: "0.000001", + completion: "0.000004", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, + "axon-mini": { + id: "axon-mini", + name: "Axon Mini", + description: + "Axon Mini is an general purpose super intelligent LLM coding model for low-effort day-to-day tasks", + input_modalities: ["text"], + context_length: 256000, + max_output_length: 16384, + output_modalities: ["text"], + supported_sampling_parameters: [ + "temperature", + "top_p", + "top_k", + "repetition_penalty", + "frequency_penalty", + "presence_penalty", + "seed", + "stop", + ], + supported_features: ["tools", "structured_outputs", "web_search"], + openrouter: { + slug: "matterai/axon", + }, + datacenters: [{ country_code: "US" }], + created: 1750426201, + owned_by: "matterai", + pricing: { + prompt: "2.5e-7", + completion: "0.000001", + image: "0", + request: "0", + input_cache_reads: "0", + input_cache_writes: "0", + }, + }, + // "axon-code-pro": { + // id: "axon-code-pro", + // name: "Axon Code Pro", + // description: "Axon Code Pro is a heavy intelligent LLM model for coding tasks", + // input_modalities: ["text"], + // context_length: 256000, + // max_output_length: 32768, + // output_modalities: ["text"], + // supported_sampling_parameters: [ + // "temperature", + // "top_p", + // "top_k", + // "repetition_penalty", + // "frequency_penalty", + // "presence_penalty", + // "seed", + // "stop", + // ], + // supported_features: ["tools", "structured_outputs", "web_search"], + // openrouter: { + // slug: "matterai/axon", + // }, + // datacenters: [{ country_code: "US" }], + // created: 1750426201, + // owned_by: "matterai", + // pricing: { + // prompt: "0.000001", + // completion: "0.000004", + // image: "0", + // request: "0", + // input_cache_reads: "0", + // input_cache_writes: "0", + // }, + // }, +} diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 32b098f44c..bea2aed9b3 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -55,6 +55,267 @@ Common tool calls and explanations - If multiple matches exist, either refine \`old_string\` or set \`replace_all\` to true when you intend to change every occurrence. - The tool shows a diff before applying changes so you can confirm the result. +## read_file Tool Usage + +The \`read_file\` tool reads specific line ranges from one or more files. This is used to examine code before making changes or to discuss specific sections. + +### CRITICAL: Line Ranges Are Always Required + +**You MUST always specify line ranges. You cannot read entire files.** + +- \`line_ranges\` is a required array of strings +- Each string must follow the format: \`"start-end"\` (e.g., \`"1-50"\`, \`"25-75"\`) +- Maximum 100 lines per request across all files +- Use search_files first if you don't know which lines to read + +### Parameters Schema +\`\`\`typescript +{ + files: [ + { + path: string, // File path (always quoted) + line_ranges: string[] // Array of "start-end" strings (always quoted) + } + ] +} +\`\`\` + +### JSON String Rules for line_ranges + +**Each line range MUST be a quoted string in "number-number" format:** + +✅ **CORRECT** - Valid JSON: +\`\`\`json +{"path": "src/App.js", "line_ranges": ["1-50"]} +{"path": "src/App.js", "line_ranges": ["1-30", "45-60"]} +\`\`\` + +❌ **INCORRECT** - Invalid JSON: +\`\`\`json +{"path": "src/App.js", "line_ranges": [1-50]} +{"path": "src/App.js", "line_ranges": ["1"-"50"]} +{"path": "src/App.js", "line_ranges": [1, 50]} +\`\`\` + +### Complete Examples + +**Read first 50 lines of a single file:** +\`\`\`json +{ + "files": [ + { + "path": "src/components/Header.jsx", + "line_ranges": ["1-50"] + } + ] +} +\`\`\` + +**Read multiple ranges from one file:** +\`\`\`json +{ + "files": [ + { + "path": "src/App.js", + "line_ranges": ["1-20", "50-75", "100-120"] + } + ] +} +\`\`\` + +**Read from multiple files (batch related files):** +\`\`\`json +{ + "files": [ + { + "path": "src/services/api.js", + "line_ranges": ["1-40"] + }, + { + "path": "src/services/auth.js", + "line_ranges": ["1-30"] + } + ] +} +\`\`\` + +**Read specific function after searching:** +\`\`\`json +{ + "files": [ + { + "path": "src/utils/helpers.js", + "line_ranges": ["45-68"] + } + ] +} +\`\`\` + +### Line Range Format Rules + +1. **Must be a string**: \`"10-20"\` not \`10-20\` +2. **Use hyphen separator**: \`"1-50"\` not \`"1:50"\` or \`"1,50"\` +3. **Start before end**: \`"1-50"\` not \`"50-1"\` +4. **Both numbers required**: \`"1-50"\` not \`"1-"\` or \`"-50"\` +5. **No spaces**: \`"1-50"\` not \`"1 - 50"\` + +### Common Line Range Patterns + +| Use Case | line_ranges Example | +|----------|-------------------| +| Read from start | \`["1-50"]\` | +| Read middle section | \`["100-150"]\` | +| Read end of file | \`["450-500"]\` | +| Multiple sections | \`["1-30", "60-90"]\` | +| Single function | \`["45-68"]\` | + +### Workflow: When You Don't Know Line Numbers + +**Step 1:** Use \`search_files\` to find the code: +\`\`\`json +{ + "path": "src", + "regex": "function handleSubmit", + "file_pattern": "*.js" +} +\`\`\` + +**Step 2:** Note the line number from search results (e.g., line 45) + +**Step 3:** Read that section with \`read_file\`: +\`\`\`json +{ + "files": [ + { + "path": "src/components/Form.js", + "line_ranges": ["40-80"] + } + ] +} +\`\`\` + +### 100 Line Limit + +**You can read up to 100 lines total per request.** + +Valid (90 lines total): +\`\`\`json +{ + "files": [ + { + "path": "file1.js", + "line_ranges": ["1-50"] + }, + { + "path": "file2.js", + "line_ranges": ["1-40"] + } + ] +} +\`\`\` + +Invalid (150 lines total): +\`\`\`json +{ + "files": [ + { + "path": "file1.js", + "line_ranges": ["1-100"] + }, + { + "path": "file2.js", + "line_ranges": ["1-50"] + } + ] +} +\`\`\` + +### Batch Related Files + +When examining related code, read multiple files in one request: +\`\`\`json +{ + "files": [ + { + "path": "src/components/Button.jsx", + "line_ranges": ["1-40"] + }, + { + "path": "src/styles/Button.css", + "line_ranges": ["1-30"] + }, + { + "path": "src/components/Button.test.js", + "line_ranges": ["1-30"] + } + ] +} +\`\`\` + +### Error Prevention Checklist + +Before generating the tool call, verify: +- ✅ \`line_ranges\` is an array: \`["1-50"]\` not \`"1-50"\` +- ✅ Each range is a quoted string: \`"1-50"\` not \`1-50\` +- ✅ Format is \`"number-number"\`: \`"1-50"\` not \`"1:50"\` +- ✅ Total lines ≤ 100 across all files +- ✅ \`line_ranges\` array is not empty +- ✅ Start line ≤ end line in each range + +### Common Mistakes to Avoid + +❌ **Unquoted ranges** (Invalid JSON): +\`\`\`json +{"path": "file.js", "line_ranges": [1-50]} +\`\`\` + +❌ **Wrong format**: +\`\`\`json +{"path": "file.js", "line_ranges": ["1:50"]} +{"path": "file.js", "line_ranges": ["1,50"]} +\`\`\` + +❌ **Array of numbers instead of strings**: +\`\`\`json +{"path": "file.js", "line_ranges": [1, 50]} +\`\`\` + +❌ **Missing line_ranges**: +\`\`\`json +{"path": "file.js"} +\`\`\` + +❌ **Empty line_ranges**: +\`\`\`json +{"path": "file.js", "line_ranges": []} +\`\`\` + +### Remember + +**Line ranges are strings in "start-end" format. Always quote them: \`"1-50"\`, never \`1-50\`** + +### Examples of Common Errors + +❌ **WRONG** (unquoted range): +\`\`\`json +{"files": [{"path": "src/App.js", "line_ranges": [1-50]}]} +\`\`\` + +✅ **CORRECT** (quoted range): +\`\`\`json +{"files": [{"path": "src/App.js", "line_ranges": ["1-50"]}]} +\`\`\` + +❌ **WRONG** (wrong separator): +\`\`\`json +{"files": [{"path": "src/App.js", "line_ranges": ["1:50"]}]} +\`\`\` + +✅ **CORRECT** (hyphen separator): +\`\`\`json +{"files": [{"path": "src/App.js", "line_ranges": ["1-50"]}]} +\`\`\` + # execute_command The \`execute_command\` tool runs CLI commands on the user's system. It allows Axon Code to perform system operations, install dependencies, build projects, start servers, and execute other terminal-based tasks needed to accomplish user objectives. diff --git a/src/core/prompts/tools/native-tools/index.ts b/src/core/prompts/tools/native-tools/index.ts index 7af433626c..cf9365aca9 100644 --- a/src/core/prompts/tools/native-tools/index.ts +++ b/src/core/prompts/tools/native-tools/index.ts @@ -6,7 +6,7 @@ import fetchInstructions from "./fetch_instructions" import listCodeDefinitionNames from "./list_code_definition_names" import listFiles from "./list_files" import newTask from "./new_task" -import { read_file_single } from "./read_file" +import { read_file_multi, read_file_single } from "./read_file" import runSlashCommand from "./run_slash_command" // import searchAndReplace from "./search_and_replace" import searchFiles from "./search_files" @@ -30,7 +30,7 @@ export const nativeTools = [ listCodeDefinitionNames, listFiles, newTask, - read_file_single, + read_file_multi, runSlashCommand, // searchAndReplace, searchFiles, diff --git a/src/package.json b/src/package.json index 326a122ef2..38297cc47a 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "4.117.0", + "version": "4.119.0", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx index 97cfa76a7b..eea9ffb0fc 100644 --- a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx +++ b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx @@ -4,7 +4,7 @@ import { OPENROUTER_DEFAULT_PROVIDER_NAME, type ProviderSettings } from "@roo-co import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" import { cn } from "@src/lib/utils" -import { prettyModelName } from "../../../utils/prettyModelName" +import { prettyModelName, getModelCredits } from "../../../utils/prettyModelName" import { useProviderModels } from "../hooks/useProviderModels" import { getModelIdKey, getSelectedModelId } from "../hooks/useSelectedModel" import { usePreferredModels } from "@/components/ui/hooks/kilocode/usePreferredModels" @@ -28,11 +28,16 @@ export const ModelSelector = ({ currentApiConfigName, apiConfiguration, fallback const modelsIds = usePreferredModels(providerModels) const options = useMemo(() => { const missingModelIds = modelsIds.indexOf(selectedModelId) >= 0 ? [] : [selectedModelId] - return missingModelIds.concat(modelsIds).map((modelId) => ({ - value: modelId, - label: providerModels[modelId]?.displayName ?? prettyModelName(modelId), - type: DropdownOptionType.ITEM, - })) + return missingModelIds.concat(modelsIds).map((modelId) => { + const baseLabel = providerModels[modelId]?.displayName ?? prettyModelName(modelId) + const credits = getModelCredits(modelId) + const label = credits ? `${baseLabel} ${credits}` : baseLabel + return { + value: modelId, + label, + type: DropdownOptionType.ITEM, + } + }) }, [modelsIds, providerModels, selectedModelId]) const disabled = isLoading || isError diff --git a/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts b/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts index 1f13373519..d92b6bddb6 100644 --- a/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts +++ b/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts @@ -107,6 +107,40 @@ const KILO_CODE_MODELS: Record = { input_cache_writes: "0", }, }, + // "axon-code-pro": { + // id: "axon-code-pro", + // name: "Axon Code Pro", + // description: "Axon Code Pro is a heavy intelligent LLM model for coding tasks", + // input_modalities: ["text"], + // context_length: 256000, + // max_output_length: 32768, + // output_modalities: ["text"], + // supported_sampling_parameters: [ + // "temperature", + // "top_p", + // "top_k", + // "repetition_penalty", + // "frequency_penalty", + // "presence_penalty", + // "seed", + // "stop", + // ], + // supported_features: ["tools", "structured_outputs", "web_search"], + // openrouter: { + // slug: "matterai/axon", + // }, + // datacenters: [{ country_code: "US" }], + // created: 1750426201, + // owned_by: "matterai", + // pricing: { + // prompt: "0.000001", + // completion: "0.000004", + // image: "0", + // request: "0", + // input_cache_reads: "0", + // input_cache_writes: "0", + // }, + // }, } const parsePrice = (value?: string): number | undefined => { diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 869717b540..74ceb8c37e 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -123,7 +123,7 @@ } }, "selectMode": "Select mode for interaction", - "selectApiConfig": "Select API configuration", + "selectApiConfig": "Select Model", "selectModelConfig": "Select model", "enhancePrompt": "Enhance prompt with additional context", "modeSelector": { diff --git a/webview-ui/src/utils/prettyModelName.ts b/webview-ui/src/utils/prettyModelName.ts index 86af618803..4c0d948e98 100644 --- a/webview-ui/src/utils/prettyModelName.ts +++ b/webview-ui/src/utils/prettyModelName.ts @@ -1,3 +1,10 @@ +// Hardcoded credits information for Axon models +const AXON_MODEL_CREDITS: Record = { + "axon-mini": "(0.5x)", + "axon-code": "(1x)", + // "axon-code-pro": "(1.25x)", +} + export const prettyModelName = (modelId: string): string => { if (!modelId) { return "" @@ -20,3 +27,13 @@ export const prettyModelName = (modelId: string): string => { return [[formattedProject, formattedName].filter(Boolean).join(" / "), formattedTag].join(" ") } + +// Function to get credits for Axon models +export const getModelCredits = (modelId: string): string | null => { + // Check if this is an Axon model + if (modelId.startsWith("axon-") || modelId === "axon-code" || modelId === "axon-code-pro") { + return AXON_MODEL_CREDITS[modelId] || null + } + + return null +}