From 6afb392daadea500077952ebdb6773537d6f4ae5 Mon Sep 17 00:00:00 2001 From: Zhanibek Datbayev Date: Mon, 12 Oct 2026 19:19:13 -0700 Subject: [PATCH] feat(models): add canonical Drex 2.0 decision model --- models/nace-ai/drex-v2.0.toml | 40 +++++++++++++++++++ .../core/src/sync/providers/openrouter.ts | 5 ++- packages/core/test/sync.test.ts | 22 ++++++++++ .../openrouter/models/nace-ai/drex-v2.0.toml | 20 +--------- 4 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 models/nace-ai/drex-v2.0.toml diff --git a/models/nace-ai/drex-v2.0.toml b/models/nace-ai/drex-v2.0.toml new file mode 100644 index 00000000000..d4464cc39a7 --- /dev/null +++ b/models/nace-ai/drex-v2.0.toml @@ -0,0 +1,40 @@ +# Sources: +# - https://huggingface.co/nace-ai/drex-v2.0 +# - https://github.com/nace-ai/drex-decision-models/blob/main/docs/context-length.md +# - https://console.nace.ai/docs/drex/reference/models +# Context is the model's maximum state-plus-one-question length, not the +# first-party API's smaller Drex 2.0 limit before fallback to Drex 1.5. +name = "Drex 2.0" +type = "decision" +description = "35B mixture-of-experts System One decision model with 3B active parameters, returning typed, calibrated probabilities over text or structured state without generating text" +release_date = "2026-10-06" +last_updated = "2026-10-12" +attachment = false +reasoning = false +temperature = false +tool_call = false +structured_output = true +open_weights = true +license = "Nace.AI Open RAIL-M" + +[limit] +context = 131_072 +output = 0 + +[modalities] +input = ["text"] +output = ["text"] + +[[links]] +label = "Documentation" +url = "https://github.com/nace-ai/drex-decision-models" +type = "docs" + +[[links]] +label = "License" +url = "https://huggingface.co/nace-ai/drex-v2.0/blob/main/LICENSE" +type = "license" + +[[weights]] +url = "https://huggingface.co/nace-ai/drex-v2.0" +format = "safetensors" diff --git a/packages/core/src/sync/providers/openrouter.ts b/packages/core/src/sync/providers/openrouter.ts index f90874363fa..4bfa6d06306 100644 --- a/packages/core/src/sync/providers/openrouter.ts +++ b/packages/core/src/sync/providers/openrouter.ts @@ -266,10 +266,11 @@ export function buildOpenRouterModel( const limit = { context, input: existing?.limit?.input, - output: OUTPUT_LIMIT_OVERRIDES[model.id] + // Decision models evaluate state without generating completion tokens. + output: decision ? 0 : (OUTPUT_LIMIT_OVERRIDES[model.id] ?? model.top_provider.max_completion_tokens ?? existing?.limit?.output - ?? context, + ?? context), }; const canonical = existing?.base_model ?? baseModel ?? resolveCanonicalBaseModel(model.id); diff --git a/packages/core/test/sync.test.ts b/packages/core/test/sync.test.ts index ae322e72769..e5396fc6d9e 100644 --- a/packages/core/test/sync.test.ts +++ b/packages/core/test/sync.test.ts @@ -5189,6 +5189,7 @@ test("syncs OpenRouter decision models with no supported parameters as decision description: "Decision model for typed evaluation of shared state", modalities: { input: ["text", "image"], output: ["text"] }, cost: { input: 0.15, output: 0 }, + limit: { context: 65_536, output: 0 }, }); expect(openrouter.translateModel(openRouterModel(), { existing: () => undefined, @@ -5214,6 +5215,27 @@ test("factors OpenRouter decision models onto decision lab metadata without a ch expect(translated?.model).toMatchObject({ base_model: "liquid/d1", cost: { input: 0.04, output: 0 } }); expect(translated?.model).not.toHaveProperty("type"); // inherited from lab metadata expect(translated?.model).not.toHaveProperty("description"); + expect(translated?.model).not.toHaveProperty("limit"); +}); + +test("inherits canonical Drex2 metadata instead of retaining an OpenRouter completion limit", () => { + for (const existing of [undefined, { limit: { output: 117_964 } }]) { + const model = buildOpenRouterModel(openRouterModel({ + id: "nace-ai/drex-v2.0", + name: "Nace AI: Drex v2.0", + knowledge_cutoff: null, + context_length: 131_072, + architecture: { input_modalities: ["text"], output_modalities: ["decisions"] }, + pricing: { prompt: "0.00000006", completion: "0" }, + top_provider: { context_length: 131_072, max_completion_tokens: 117_964 }, + supported_parameters: [], + }), existing); + + expect(model).toMatchObject({ base_model: "nace-ai/drex-v2.0", cost: { input: 0.06, output: 0 } }); + for (const inherited of ["type", "description", "limit", "structured_output", "open_weights"]) { + expect(model).not.toHaveProperty(inherited); + } + } }); test("parses nullable EmpirioLabs release dates", () => { diff --git a/providers/openrouter/models/nace-ai/drex-v2.0.toml b/providers/openrouter/models/nace-ai/drex-v2.0.toml index 5d8ec7929f5..7575033ede9 100644 --- a/providers/openrouter/models/nace-ai/drex-v2.0.toml +++ b/providers/openrouter/models/nace-ai/drex-v2.0.toml @@ -1,23 +1,5 @@ -type = "decision" -name = "Drex v2.0" -description = "Decision model for typed evaluation of shared state" -release_date = "2026-10-12" -last_updated = "2026-10-12" -attachment = false -reasoning = false -temperature = false -tool_call = false -structured_output = false -open_weights = false +base_model = "nace-ai/drex-v2.0" [cost] input = 0.06 output = 0 - -[limit] -context = 131_072 -output = 117_964 - -[modalities] -input = ["text"] -output = ["text"]