diff --git a/.github/workflows/sync-models.yml b/.github/workflows/sync-models.yml index 287d82dc40..20da3e8447 100644 --- a/.github/workflows/sync-models.yml +++ b/.github/workflows/sync-models.yml @@ -77,6 +77,7 @@ jobs: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} + HYPER_API_KEY: ${{ secrets.HYPER_API_KEY }} CLOUDFLARE_WORKERS_AI_SYNC_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_WORKERS_AI_SYNC_ACCOUNT_ID }} CLOUDFLARE_WORKERS_AI_SYNC_API_TOKEN: ${{ secrets.CLOUDFLARE_WORKERS_AI_SYNC_API_TOKEN }} diff --git a/packages/core/src/sync/index.ts b/packages/core/src/sync/index.ts index e67f3144f4..c61a95b56a 100644 --- a/packages/core/src/sync/index.ts +++ b/packages/core/src/sync/index.ts @@ -12,6 +12,7 @@ import { crossmodel } from "./providers/crossmodel.js"; import { deepinfra } from "./providers/deepinfra.js"; import { digitalocean } from "./providers/digitalocean.js"; import { google } from "./providers/google.js"; +import { hyper } from "./providers/hyper.js"; import { huggingface } from "./providers/huggingface.js"; import { llmgateway } from "./providers/llmgateway.js"; import { openai } from "./providers/openai.js"; @@ -97,6 +98,7 @@ export const providers: { deepinfra: SyncProvider; digitalocean: SyncProvider; google: SyncProvider; + hyper: SyncProvider; huggingface: SyncProvider; llmgateway: SyncProvider; openai: SyncProvider; @@ -115,6 +117,7 @@ export const providers: { deepinfra, digitalocean, google, + hyper, huggingface, llmgateway, openai, @@ -129,7 +132,7 @@ export const providers: { export const groups = { aggregators: ["crossmodel", "huggingface", "llmgateway", "openrouter", "vercel"], cloudflare: ["cloudflare-workers-ai"], - direct: ["anthropic", "baseten", "chutes", "deepinfra", "digitalocean", "google", "openai", "ovhcloud", "venice", "wandb", "xai"], + direct: ["anthropic", "baseten", "chutes", "deepinfra", "digitalocean", "google", "hyper", "openai", "ovhcloud", "venice", "wandb", "xai"], } as const; type ProviderID = keyof typeof providers; diff --git a/packages/core/src/sync/providers/hyper.ts b/packages/core/src/sync/providers/hyper.ts new file mode 100644 index 0000000000..9eb3751082 --- /dev/null +++ b/packages/core/src/sync/providers/hyper.ts @@ -0,0 +1,252 @@ +import { z } from "zod"; + +import type { ExistingModel, SyncProvider, SyncedFullModel, SyncedModel } from "../index.js"; +import { factorBaseModel } from "./openrouter.js"; +import { resolveVeniceBaseModel } from "./venice.js"; + +const API_ENDPOINT = "https://hyper.charm.land/v1/models"; +const PROVIDER_ENDPOINT = "https://hyper.charm.land/v1/provider"; + +const PricingValue = z.union([z.string(), z.number()]); + +const Pricing = z.object({ + prompt: PricingValue.optional(), + completion: PricingValue.optional(), + input_cache_read: PricingValue.optional(), + input_cache_reads: PricingValue.optional(), + input_cache_write: PricingValue.optional(), + input_cache_writes: PricingValue.optional(), + internal_reasoning: PricingValue.optional(), +}).passthrough(); + +const ReasoningEffort = z.enum([ + "default", + "max", + "low", + "high", + "none", + "medium", + "minimal", + "xhigh", +]); + +export const HyperModel = z.object({ + id: z.string(), + created: z.number(), + display_name: z.string(), + supports_reasoning: z.boolean(), + supports_reasoning_effort: z.boolean(), + reasoning_effort_levels: z.array(z.string()).optional(), + supports_attachments: z.boolean(), + context_window: z.number(), + max_output_tokens: z.number(), + pricing: Pricing.optional(), + cost_per_1m_in: z.number().optional(), + cost_per_1m_out: z.number().optional(), + cost_per_1m_in_cached: z.number().optional(), + cost_per_1m_out_cached: z.number().optional(), +}).passthrough(); + +export const HyperResponse = z.object({ + data: z.array(HyperModel), +}).passthrough(); + +const HyperProviderModel = z.object({ + id: z.string(), + cost_per_1m_in: z.number(), + cost_per_1m_out: z.number(), + cost_per_1m_in_cached: z.number().optional(), + cost_per_1m_out_cached: z.number().optional(), +}).passthrough(); + +const HyperProviderResponse = z.object({ + models: z.array(HyperProviderModel), +}).passthrough(); + +export type HyperModel = z.infer; + +const BASE_MODEL_ALIASES: Record = { + "llama-4-maverick-17b-128e-instruct-fp8": "meta/llama-4-maverick-17b-instruct", + "minimax-m2.7": "minimax/MiniMax-M2.7", + "qwen3-coder-480b-a35b-instruct-int4-mixed-ar": "alibaba/qwen3-coder-480b-a35b-instruct", + "qwen3.6-max": "alibaba/qwen3.6-max-preview", +}; + +export const hyper = { + id: "hyper", + name: "Charm Hyper", + modelsDir: "providers/hyper/models", + preserveBaseModels: false, + async fetchModels() { + const headers = process.env.HYPER_API_KEY !== undefined + ? { Authorization: `Bearer ${process.env.HYPER_API_KEY}` } + : undefined; + const [modelsResponse, providerResponse] = await Promise.all([ + fetch(API_ENDPOINT, { headers }), + fetch(PROVIDER_ENDPOINT), + ]); + if (!modelsResponse.ok) { + throw new Error(`Hyper models request failed: ${modelsResponse.status} ${modelsResponse.statusText}`); + } + + const modelsRaw = await modelsResponse.json(); + const parsed = HyperResponse.parse(modelsRaw); + if (!providerResponse.ok) return modelsRaw; + + const pricingById = new Map( + HyperProviderResponse.parse(await providerResponse.json()).models.map((model) => [model.id, model]), + ); + + return { + ...modelsRaw, + data: parsed.data.map((model) => enrichPricing(model, pricingById.get(model.id))), + }; + }, + parseModels(raw) { + return HyperResponse.parse(raw).data; + }, + translateModel(model, context) { + const authored = context.authored(model.id); + const baseModel = authored?.base_model + ?? BASE_MODEL_ALIASES[model.id] + ?? resolveVeniceBaseModel(model.id, model.display_name) + ?? undefined; + return { + id: model.id, + model: buildHyperModel(model, authored, baseModel), + }; + }, +} satisfies SyncProvider; + +function dateFromTimestamp(timestamp: number) { + return new Date(timestamp * 1000).toISOString().slice(0, 10); +} + +function reasoningOptions(model: HyperModel) { + if (!model.supports_reasoning) return []; + if (model.supports_reasoning_effort) { + const values = model.reasoning_effort_levels?.filter(isReasoningEffort) ?? []; + if (values.length > 0) return [{ type: "effort" as const, values }]; + } + // Hyper advertises reasoning but documents no toggle or effort control for these models. + return []; +} + +function isReasoningEffort(value: string): value is z.infer { + return ReasoningEffort.safeParse(value).success; +} + +function hasApiPricing(model: HyperModel) { + return ( + model.pricing?.prompt !== undefined && model.pricing?.completion !== undefined + ) || ( + model.cost_per_1m_in !== undefined && model.cost_per_1m_out !== undefined + ); +} + +function enrichPricing( + model: HyperModel, + provider: z.infer | undefined, +): HyperModel { + if (hasApiPricing(model) || provider === undefined) return model; + return { + ...model, + cost_per_1m_in: provider.cost_per_1m_in, + cost_per_1m_out: provider.cost_per_1m_out, + cost_per_1m_in_cached: provider.cost_per_1m_in_cached, + cost_per_1m_out_cached: provider.cost_per_1m_out_cached, + }; +} + +function parsePrice(value: string | number | undefined) { + if (value === undefined) return undefined; + if (typeof value === "number") { + return Number.isFinite(value) && value >= 0 ? value : undefined; + } + const number = Number(value); + return Number.isFinite(number) && number >= 0 + ? Math.round(number * 1_000_000_000_000) / 1_000_000 + : undefined; +} + +function positivePrice(value: number | undefined) { + return value !== undefined && value > 0 ? value : undefined; +} + +function buildCost(model: HyperModel, existing: ExistingModel["cost"] | undefined) { + const fromProviderFields = model.cost_per_1m_in !== undefined && model.cost_per_1m_out !== undefined + ? { + input: model.cost_per_1m_in, + output: model.cost_per_1m_out, + // Hyper /provider names these fields by token direction, not catalog semantics: + // out_cached is the cheap cache-hit read price; in_cached is the cache-write price. + cache_read: model.cost_per_1m_out_cached, + cache_write: model.cost_per_1m_in_cached, + reasoning: undefined as number | undefined, + } + : undefined; + + const pricing = model.pricing; + const fromPricingObject = pricing?.prompt !== undefined && pricing?.completion !== undefined + ? { + input: parsePrice(pricing.prompt), + output: parsePrice(pricing.completion), + cache_read: parsePrice(pricing.input_cache_read ?? pricing.input_cache_reads), + cache_write: parsePrice(pricing.input_cache_write ?? pricing.input_cache_writes), + reasoning: parsePrice(pricing.internal_reasoning), + } + : undefined; + + const resolved = fromProviderFields ?? fromPricingObject; + if (resolved?.input === undefined || resolved.output === undefined) return existing; + + const providerPricing = fromProviderFields !== undefined; + + return { + input: resolved.input, + output: resolved.output, + cache_read: providerPricing + ? positivePrice(resolved.cache_read) + : positivePrice(resolved.cache_read) ?? existing?.cache_read, + cache_write: providerPricing + ? positivePrice(resolved.cache_write) + : positivePrice(resolved.cache_write) ?? existing?.cache_write, + reasoning: positivePrice(resolved.reasoning) ?? existing?.reasoning, + input_audio: existing?.input_audio, + output_audio: existing?.output_audio, + tiers: existing?.tiers, + }; +} + +export function buildHyperModel( + model: HyperModel, + authored: ExistingModel | undefined, + baseModel: string | undefined, + today = new Date().toISOString().slice(0, 10), +): SyncedModel { + const limit = { + context: model.context_window, + input: authored?.limit?.input, + output: model.max_output_tokens, + }; + const values: Partial = { + name: authored?.name ?? model.display_name, + attachment: model.supports_attachments, + reasoning: model.supports_reasoning, + reasoning_options: reasoningOptions(model), + release_date: authored?.release_date ?? dateFromTimestamp(model.created), + last_updated: authored?.last_updated ?? today, + interleaved: authored?.interleaved, + cost: buildCost(model, authored?.cost), + limit, + modalities: model.supports_attachments + ? authored?.modalities + : authored?.modalities ?? { input: ["text"], output: ["text"] }, + }; + + if (baseModel === undefined) { + throw new Error(`Hyper model ${model.id} has no matching base_model metadata`); + } + + return factorBaseModel(baseModel, values, limit, authored?.base_model_omit); +} diff --git a/packages/core/test/sync.test.ts b/packages/core/test/sync.test.ts index 61bb21792c..7ed7ba30ee 100644 --- a/packages/core/test/sync.test.ts +++ b/packages/core/test/sync.test.ts @@ -30,8 +30,77 @@ import { openai, parseOpenAIModels } from "../src/sync/providers/openai.js"; import { resolveVeniceBaseModel } from "../src/sync/providers/venice.js"; import { buildVercelModel, vercel } from "../src/sync/providers/vercel.js"; import { buildWandbModel, type WandbModel } from "../src/sync/providers/wandb.js"; +import { buildHyperModel, type HyperModel } from "../src/sync/providers/hyper.js"; import { buildXAIModel } from "../src/sync/providers/xai.js"; +test("syncs Hyper cache read and write from provider pricing fields", () => { + const model = hyperModel({ + id: "qwen3.6-flash", + cost_per_1m_in: 1, + cost_per_1m_out: 4, + cost_per_1m_in_cached: 1.25, + cost_per_1m_out_cached: 0.1, + }); + + expect(buildHyperModel(model, undefined, "alibaba/qwen3.6-flash")).toMatchObject({ + cost: { input: 1, output: 4, cache_read: 0.1, cache_write: 1.25 }, + }); +}); + +test("preserves hand-authored Hyper cost fields the API does not expose", () => { + const existing = { + cost: { + input: 1, + output: 2, + cache_write: 0.375, + tiers: [{ tier: { type: "context" as const, size: 200_000 }, input: 3, output: 4 }], + }, + release_date: "2026-01-01", + last_updated: "2026-01-01", + }; + + expect(buildHyperModel(hyperModel({ + id: "minimax-m2.7", + cost_per_1m_in: 0.82, + cost_per_1m_out: 2.64, + cost_per_1m_in_cached: 0.41, + cost_per_1m_out_cached: 0, + }), existing, "minimax/MiniMax-M2.7")).toMatchObject({ + cost: { + input: 0.82, + output: 2.64, + cache_write: 0.41, + tiers: existing.cost.tiers, + }, + }); +}); + +test("emits text-only modalities when Hyper disables attachments", () => { + const built = buildHyperModel(hyperModel({ + id: "gemma-4-26b-a4b-it", + supports_attachments: false, + }), undefined, "google/gemma-4-26b-a4b-it"); + + expect(built).toMatchObject({ + attachment: false, + modalities: { input: ["text"] }, + }); +}); + +test("defaults Hyper reasoning models without effort levels to empty reasoning options", () => { + expect(buildHyperModel(hyperModel({ supports_reasoning_effort: false }), undefined, "minimax/MiniMax-M2.7")) + .toMatchObject({ reasoning_options: [] }); +}); + +test("preserves Hyper display name overrides", () => { + expect(buildHyperModel(hyperModel({ + id: "qwen3.6-max", + display_name: "Qwen3.6-Max", + }), { name: "Qwen3.6-Max" }, "alibaba/qwen3.6-max-preview")).toMatchObject({ + name: "Qwen3.6-Max", + }); +}); + function anthropicModel(overrides: Partial = {}): AnthropicModel { return { id: "claude-sonnet-5", @@ -1061,6 +1130,21 @@ function llmGatewayModel(overrides: Partial = {}): LLMGatewayMo }; } +function hyperModel(overrides: Partial = {}): HyperModel { + return { + id: "deepseek-v4-flash", + created: 1_780_592_628, + display_name: "DeepSeek V4 Flash", + supports_reasoning: true, + supports_reasoning_effort: true, + reasoning_effort_levels: ["high", "xhigh"], + supports_attachments: false, + context_window: 1_000_000, + max_output_tokens: 384_000, + ...overrides, + }; +} + function openRouterModel(overrides: Partial = {}): OpenRouterModel { return { id: "anthropic/claude-sonnet-5", diff --git a/providers/hyper/logo.svg b/providers/hyper/logo.svg new file mode 100644 index 0000000000..ca3977b6eb --- /dev/null +++ b/providers/hyper/logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/providers/hyper/models/deepseek-v4-flash.toml b/providers/hyper/models/deepseek-v4-flash.toml new file mode 100644 index 0000000000..259f19f8a7 --- /dev/null +++ b/providers/hyper/models/deepseek-v4-flash.toml @@ -0,0 +1,15 @@ +base_model = "deepseek/deepseek-v4-flash" +release_date = "2026-07-06" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["none", "high", "xhigh"] + +[cost] +input = 0.14 +output = 0.28 +cache_read = 0.03 diff --git a/providers/hyper/models/deepseek-v4-pro.toml b/providers/hyper/models/deepseek-v4-pro.toml new file mode 100644 index 0000000000..417e5020d7 --- /dev/null +++ b/providers/hyper/models/deepseek-v4-pro.toml @@ -0,0 +1,15 @@ +base_model = "deepseek/deepseek-v4-pro" +release_date = "2026-07-06" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["none", "high", "xhigh"] + +[cost] +input = 1.74 +output = 3.48 +cache_read = 0.15 diff --git a/providers/hyper/models/gemma-4-26b-a4b-it.toml b/providers/hyper/models/gemma-4-26b-a4b-it.toml new file mode 100644 index 0000000000..a7eaa94835 --- /dev/null +++ b/providers/hyper/models/gemma-4-26b-a4b-it.toml @@ -0,0 +1,21 @@ +base_model = "google/gemma-4-26b-a4b-it" +name = "Gemma 4 26B A4B" +release_date = "2026-04-30" +last_updated = "2026-07-10" +attachment = false +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.12 +output = 0.426 +cache_write = 0.06 + +[limit] +context = 256_000 +output = 25_600 + +[modalities] +input = ["text"] diff --git a/providers/hyper/models/glm-5.1.toml b/providers/hyper/models/glm-5.1.toml new file mode 100644 index 0000000000..71235ecb7f --- /dev/null +++ b/providers/hyper/models/glm-5.1.toml @@ -0,0 +1,18 @@ +base_model = "zhipuai/glm-5.1" +release_date = "2026-06-04" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["high", "max"] + +[cost] +input = 1.4 +output = 4.4 +cache_read = 0.26 + +[limit] +context = 202_800 diff --git a/providers/hyper/models/glm-5.2.toml b/providers/hyper/models/glm-5.2.toml new file mode 100644 index 0000000000..2861da7c35 --- /dev/null +++ b/providers/hyper/models/glm-5.2.toml @@ -0,0 +1,18 @@ +base_model = "zhipuai/glm-5.2" +release_date = "2026-06-30" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["high", "max"] + +[cost] +input = 1.4 +output = 4.4 +cache_read = 0.14 + +[limit] +context = 1_048_576 diff --git a/providers/hyper/models/glm-5.toml b/providers/hyper/models/glm-5.toml new file mode 100644 index 0000000000..a4eb309245 --- /dev/null +++ b/providers/hyper/models/glm-5.toml @@ -0,0 +1,16 @@ +base_model = "zhipuai/glm-5" +release_date = "2026-04-13" +last_updated = "2026-07-10" +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.77 +output = 2.388 +cache_write = 0.385 + +[limit] +context = 202_752 +output = 20_275 diff --git a/providers/hyper/models/gpt-oss-120b.toml b/providers/hyper/models/gpt-oss-120b.toml new file mode 100644 index 0000000000..46a8a7d23e --- /dev/null +++ b/providers/hyper/models/gpt-oss-120b.toml @@ -0,0 +1,19 @@ +base_model = "openai/gpt-oss-120b" +name = "gpt-oss-120b" +release_date = "2026-04-13" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["low", "medium", "high"] + +[cost] +input = 0.18 +output = 0.61 +cache_write = 0.09 + +[limit] +output = 13_107 diff --git a/providers/hyper/models/kimi-k2.5.toml b/providers/hyper/models/kimi-k2.5.toml new file mode 100644 index 0000000000..3840ee165a --- /dev/null +++ b/providers/hyper/models/kimi-k2.5.toml @@ -0,0 +1,18 @@ +base_model = "moonshotai/kimi-k2.5" +release_date = "2026-04-13" +last_updated = "2026-07-10" +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.477 +output = 2.535 +cache_write = 0.2385 + +[limit] +output = 26_214 + +[modalities] +input = ["text"] diff --git a/providers/hyper/models/kimi-k2.6.toml b/providers/hyper/models/kimi-k2.6.toml new file mode 100644 index 0000000000..85944b6486 --- /dev/null +++ b/providers/hyper/models/kimi-k2.6.toml @@ -0,0 +1,19 @@ +base_model = "moonshotai/kimi-k2.6" +release_date = "2026-07-03" +last_updated = "2026-07-10" + +[interleaved] +field = "reasoning_content" + +[[reasoning_options]] +type = "effort" +values = ["low", "medium", "high"] + +[cost] +input = 0.95 +output = 4 +cache_write = 0.16 + +[limit] +context = 262_000 +output = 262_000 diff --git a/providers/hyper/models/kimi-k2.7-code.toml b/providers/hyper/models/kimi-k2.7-code.toml new file mode 100644 index 0000000000..845e310243 --- /dev/null +++ b/providers/hyper/models/kimi-k2.7-code.toml @@ -0,0 +1,16 @@ +base_model = "moonshotai/kimi-k2.7-code" +release_date = "2026-07-03" +last_updated = "2026-07-10" +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.95 +output = 4 +cache_read = 0.19 + +[limit] +context = 262_000 +output = 262_000 diff --git a/providers/hyper/models/llama-3.3-70b-instruct.toml b/providers/hyper/models/llama-3.3-70b-instruct.toml new file mode 100644 index 0000000000..49320f7f8f --- /dev/null +++ b/providers/hyper/models/llama-3.3-70b-instruct.toml @@ -0,0 +1,18 @@ +base_model = "meta/llama-3.3-70b-instruct" +name = "Llama 3.3 70B Instruct" +release_date = "2026-04-30" +last_updated = "2026-07-10" +attachment = false +reasoning = true +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.6066 +output = 1.0386 +cache_write = 0.3033 + +[limit] +output = 12_800 diff --git a/providers/hyper/models/llama-4-maverick-17b-128e-instruct-fp8.toml b/providers/hyper/models/llama-4-maverick-17b-128e-instruct-fp8.toml new file mode 100644 index 0000000000..84b0ef3bb9 --- /dev/null +++ b/providers/hyper/models/llama-4-maverick-17b-128e-instruct-fp8.toml @@ -0,0 +1,22 @@ +base_model = "meta/llama-4-maverick-17b-instruct" +name = "Llama 4 Maverick 17B 128E Instruct FP8" +release_date = "2026-04-30" +last_updated = "2026-07-10" +attachment = false +reasoning = true +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.274 +output = 0.894 +cache_write = 0.137 + +[limit] +context = 430_000 +output = 43_000 + +[modalities] +input = ["text"] diff --git a/providers/hyper/models/minimax-m2.7.toml b/providers/hyper/models/minimax-m2.7.toml new file mode 100644 index 0000000000..da84763946 --- /dev/null +++ b/providers/hyper/models/minimax-m2.7.toml @@ -0,0 +1,16 @@ +base_model = "minimax/MiniMax-M2.7" +name = "MiniMax M2.7" +release_date = "2026-06-05" +last_updated = "2026-07-10" +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.82 +output = 2.64 +cache_write = 0.41 + +[limit] +output = 20_480 diff --git a/providers/hyper/models/qwen3-coder-480b-a35b-instruct-int4-mixed-ar.toml b/providers/hyper/models/qwen3-coder-480b-a35b-instruct-int4-mixed-ar.toml new file mode 100644 index 0000000000..2d09b194e2 --- /dev/null +++ b/providers/hyper/models/qwen3-coder-480b-a35b-instruct-int4-mixed-ar.toml @@ -0,0 +1,18 @@ +base_model = "alibaba/qwen3-coder-480b-a35b-instruct" +name = "Qwen3 Coder 480B A35B Instruct INT4 Mixed AR" +release_date = "2026-04-30" +last_updated = "2026-07-10" +reasoning = true +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.601 +output = 2.085 +cache_write = 0.3005 + +[limit] +context = 106_000 +output = 10_600 diff --git a/providers/hyper/models/qwen3-next-80b-a3b-instruct.toml b/providers/hyper/models/qwen3-next-80b-a3b-instruct.toml new file mode 100644 index 0000000000..c2ba22107c --- /dev/null +++ b/providers/hyper/models/qwen3-next-80b-a3b-instruct.toml @@ -0,0 +1,18 @@ +base_model = "alibaba/qwen3-next-80b-a3b-instruct" +name = "Qwen3 Next 80B A3B Instruct" +release_date = "2026-04-30" +last_updated = "2026-07-10" +reasoning = true +reasoning_options = [] + +[interleaved] +field = "reasoning_content" + +[cost] +input = 0.1175 +output = 1.136 +cache_write = 0.05875 + +[limit] +context = 262_144 +output = 26_214 diff --git a/providers/hyper/models/qwen3.6-flash.toml b/providers/hyper/models/qwen3.6-flash.toml new file mode 100644 index 0000000000..ab8b5b4f80 --- /dev/null +++ b/providers/hyper/models/qwen3.6-flash.toml @@ -0,0 +1,14 @@ +base_model = "alibaba/qwen3.6-flash" +name = "Qwen3.6-Flash" +release_date = "2026-05-20" +last_updated = "2026-07-10" +reasoning_options = [] + +[cost] +input = 1 +output = 4 +cache_read = 0.1 +cache_write = 1.25 + +[limit] +output = 64_000 diff --git a/providers/hyper/models/qwen3.6-max.toml b/providers/hyper/models/qwen3.6-max.toml new file mode 100644 index 0000000000..04d2a5a42d --- /dev/null +++ b/providers/hyper/models/qwen3.6-max.toml @@ -0,0 +1,15 @@ +base_model = "alibaba/qwen3.6-max-preview" +name = "Qwen3.6-Max" +release_date = "2026-05-20" +last_updated = "2026-07-10" +reasoning_options = [] + +[cost] +input = 2 +output = 12 +cache_read = 0.2 +cache_write = 2.5 + +[limit] +context = 256_000 +output = 64_000 diff --git a/providers/hyper/models/qwen3.6-plus.toml b/providers/hyper/models/qwen3.6-plus.toml new file mode 100644 index 0000000000..f4f97f539b --- /dev/null +++ b/providers/hyper/models/qwen3.6-plus.toml @@ -0,0 +1,15 @@ +base_model = "alibaba/qwen3.6-plus" +name = "Qwen3.6-Plus" +release_date = "2026-05-20" +last_updated = "2026-07-10" +attachment = true +reasoning_options = [] + +[cost] +input = 2 +output = 6 +cache_read = 0.2 +cache_write = 2.5 + +[limit] +output = 64_000 diff --git a/providers/hyper/models/qwen3.7-max.toml b/providers/hyper/models/qwen3.7-max.toml new file mode 100644 index 0000000000..2c66a85997 --- /dev/null +++ b/providers/hyper/models/qwen3.7-max.toml @@ -0,0 +1,13 @@ +base_model = "alibaba/qwen3.7-max" +name = "Qwen3.7-Max" +release_date = "2026-05-28" +last_updated = "2026-07-10" +reasoning_options = [] + +[cost] +input = 2.5 +output = 7.5 +cache_read = 0.5 + +[limit] +output = 64_000 diff --git a/providers/hyper/models/qwen3.7-plus.toml b/providers/hyper/models/qwen3.7-plus.toml new file mode 100644 index 0000000000..0098960906 --- /dev/null +++ b/providers/hyper/models/qwen3.7-plus.toml @@ -0,0 +1,11 @@ +base_model = "alibaba/qwen3.7-plus" +name = "Qwen3.7-Plus" +release_date = "2026-06-15" +last_updated = "2026-07-10" +attachment = true +reasoning_options = [] + +[cost] +input = 1.2 +output = 4.8 +cache_read = 0.24 diff --git a/providers/hyper/provider.toml b/providers/hyper/provider.toml new file mode 100644 index 0000000000..d6d8457ed5 --- /dev/null +++ b/providers/hyper/provider.toml @@ -0,0 +1,13 @@ +name = "Charm Hyper" +env = ["HYPER_API_KEY"] +npm = "@ai-sdk/openai-compatible" +# Raw HTTP endpoint map (source accessed 2026-07-02): Hyper uses POST +# `/v1/chat/completions` for OpenAI-compatible models, `/v1/responses` for +# OpenAI Responses, and `/v1/messages` for Anthropic-compatible models. +# GET `/v1/models` advertises `supports_reasoning`, `supports_reasoning_effort`, +# and per-model `reasoning_effort_levels`. `supports_reasoning` is a capability +# flag only; models without effort levels expose no verified caller-facing control, +# so synced `reasoning_options` stay empty unless effort levels are present. +# https://hyper.charm.land/v1/models +api = "https://hyper.charm.land/v1" +doc = "https://hyper.charm.land"