Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **Axon Eido 3 Flash 400K context option.** Added `axon-eido-3-flash-400k`,
a 400K context variant of the flash model that maps to the same
`axon-eido-3-flash` gateway model. Gated to Pro Plus and Ultra plans like
the other 400K options; the picker renders it under the 400K group and the
200K fallback resolves to the bare `axon-eido-3-flash` id.

## [6.7.4] - 2026-08-04

### Added
Expand Down
108 changes: 59 additions & 49 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@matterailab/orbcode",
"version": "6.7.4",
"version": "6.7.6",
"description": "OrbCode CLI — agentic coding in your terminal, powered by Axon models by MatterAI",
"type": "module",
"bin": {
Expand Down
50 changes: 49 additions & 1 deletion src/api/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface AxonModel {
/** USD per token */
outputPrice: number;
free: boolean;
/** Human-readable pricing shown when the gateway chooses the billable model dynamically. */
pricingLabel?: string;
/**
* Which transport serves this model. Absent (or "matterai"/"axon") routes
* through the MatterAI gateway (OpenAI `/chat/completions`). Any other value
Expand Down Expand Up @@ -141,6 +143,34 @@ export const ANTHROPIC_MODELS: Record<string, AxonModel> = {
* interactive picker for now.
*/
export const BUILTIN_AXON_MODELS: Record<string, AxonModel> = {
"axon-auto-200k": {
id: "axon-auto-200k",
gatewayModelId: "axon-auto",
name: "Axon Auto (200K context)",
description:
"Starts with Code Flash, then dynamically selects Code Flash, Mini, or Pro as the task develops.",
contextWindow: 200000,
maxOutputTokens: 64000,
supportsImages: true,
inputPrice: 0.0000005,
outputPrice: 0.0000015,
free: false,
pricingLabel: "dynamic pricing",
},
"axon-auto-400k": {
id: "axon-auto-400k",
gatewayModelId: "axon-auto",
name: "Axon Auto (400K context)",
description:
"Starts with Code Flash, then dynamically selects Code Flash, Mini, or Pro as the task develops.",
contextWindow: 400000,
maxOutputTokens: 64000,
supportsImages: true,
inputPrice: 0.0000005,
outputPrice: 0.0000015,
free: false,
pricingLabel: "dynamic pricing",
},
"axon-eido-3-flash": {
id: "axon-eido-3-flash",
name: "Axon Eido 3 Flash",
Expand All @@ -153,6 +183,19 @@ export const BUILTIN_AXON_MODELS: Record<string, AxonModel> = {
outputPrice: 0.0000015,
free: false,
},
"axon-eido-3-flash-400k": {
id: "axon-eido-3-flash-400k",
gatewayModelId: "axon-eido-3-flash",
name: "Axon Eido 3 Flash (400K context)",
description:
"Axon Eido is a fast and low cost general purpose model for low-effort day-to-day tasks",
contextWindow: 400000,
maxOutputTokens: 64000,
supportsImages: true,
inputPrice: 0.0000005,
outputPrice: 0.0000015,
free: false,
},
"axon-eido-3-code-pro-200k": {
id: "axon-eido-3-code-pro-200k",
gatewayModelId: "axon-eido-3-code-pro",
Expand Down Expand Up @@ -271,7 +314,9 @@ export function canUseEidoProModels(plan?: string): boolean {

export function is400kAxonModel(modelId: string): boolean {
return (
(modelId.startsWith("axon-eido-3-code-") ||
(modelId.startsWith("axon-auto-") ||
modelId.startsWith("axon-eido-3-code-") ||
modelId.startsWith("axon-eido-3-flash-") ||
modelId.startsWith("axon-lumen-4-code-")) &&
modelId.endsWith("-400k")
);
Expand All @@ -286,6 +331,9 @@ export function isEidoProAxonModel(modelId: string): boolean {
}

export function get200kAxonFallback(modelId: string): string {
// The 200K Flash option uses the bare id (no "-200k" suffix), so the
// generic -400k → -200k rewrite would point at a non-existent model.
if (modelId === "axon-eido-3-flash-400k") return "axon-eido-3-flash";
return modelId.replace(/-400k$/, "-200k");
}

Expand Down
4 changes: 3 additions & 1 deletion src/ui/components/ModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import { PopoverBox } from "./PopoverBox.js"

const VISIBLE_ROWS = 6
const CONTEXT_WINDOW_ORDER = [200000, 400000]
// Display order within a context-window group: Flash, Mini, Pro, Lumen.
// Display order within a context-window group: Auto, Flash, Mini, Pro, Lumen.
const DISPLAY_ORDER = [
"axon-auto",
"axon-eido-3-flash",
"axon-eido-3-code-mini",
"axon-eido-3-code-pro",
Expand All @@ -31,6 +32,7 @@ interface ModelPickerProps {
}

function formatPrice(model: AxonModel): string {
if (model.pricingLabel) return model.pricingLabel
if (model.free) return "free"
const perMillion = (price: number) => `$${(price * 1_000_000).toFixed(2)}`
return `${perMillion(model.inputPrice)} in / ${perMillion(model.outputPrice)} out per 1M tokens`
Expand Down
42 changes: 42 additions & 0 deletions test/axon-auto-models.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import assert from "node:assert/strict"
import test from "node:test"

import {
BUILTIN_AXON_MODELS,
get200kAxonFallback,
getGatewayModelId,
is400kAxonModel,
} from "../src/api/models.js"

test("Axon Auto exposes 200K and 400K dynamic-pricing choices", () => {
const auto200k = BUILTIN_AXON_MODELS["axon-auto-200k"]
const auto400k = BUILTIN_AXON_MODELS["axon-auto-400k"]

assert.equal(auto200k.contextWindow, 200_000)
assert.equal(auto400k.contextWindow, 400_000)
assert.equal(auto200k.pricingLabel, "dynamic pricing")
assert.equal(auto400k.pricingLabel, "dynamic pricing")
assert.equal(auto200k.free, false)
assert.equal(auto400k.free, false)
assert.equal(getGatewayModelId(auto200k), "axon-auto")
assert.equal(getGatewayModelId(auto400k), "axon-auto")
})

test("Auto 400K uses the existing extended-context gate and fallback", () => {
assert.equal(is400kAxonModel("axon-auto-400k"), true)
assert.equal(is400kAxonModel("axon-auto-200k"), false)
assert.equal(get200kAxonFallback("axon-auto-400k"), "axon-auto-200k")
})

test("Axon Eido 3 Flash exposes a 400K variant through the same gates", () => {
const flash400k = BUILTIN_AXON_MODELS["axon-eido-3-flash-400k"]

assert.equal(flash400k.contextWindow, 400_000)
assert.equal(flash400k.maxOutputTokens, 64_000)
assert.equal(getGatewayModelId(flash400k), "axon-eido-3-flash")
assert.equal(is400kAxonModel("axon-eido-3-flash-400k"), true)
assert.equal(is400kAxonModel("axon-eido-3-flash"), false)
// The 200K Flash option uses the bare id (no "-200k" suffix), so the
// fallback must map to that exact id rather than the generic -200k form.
assert.equal(get200kAxonFallback("axon-eido-3-flash-400k"), "axon-eido-3-flash")
})
Loading