@@ -26,18 +26,52 @@ const canopywaveAgent = new Agent({
2626 bodyTimeout : 0 ,
2727} )
2828
29- /** Map from OpenRouter model IDs to CanopyWave model IDs */
30- const CANOPYWAVE_MODEL_MAP : Record < string , string > = {
31- 'minimax/minimax-m2.5' : 'minimax/minimax-m2.5' ,
32- 'moonshotai/kimi-k2.6' : 'moonshotai/kimi-k2.6' ,
29+ // CanopyWave per-token pricing (dollars per token)
30+ interface CanopyWavePricing {
31+ inputCostPerToken : number
32+ cachedInputCostPerToken : number
33+ outputCostPerToken : number
34+ }
35+
36+ /** Single source of truth: which OpenRouter model IDs we route through
37+ * CanopyWave, the corresponding CanopyWave model ID, and per-model pricing.
38+ * Kept as one map so adding a model can't drift between routing and billing. */
39+ const CANOPYWAVE_MODELS : Record <
40+ string ,
41+ { canopywaveId : string ; pricing : CanopyWavePricing }
42+ > = {
43+ 'minimax/minimax-m2.5' : {
44+ canopywaveId : 'minimax/minimax-m2.5' ,
45+ pricing : {
46+ inputCostPerToken : 0.27 / 1_000_000 ,
47+ cachedInputCostPerToken : 0.03 / 1_000_000 ,
48+ outputCostPerToken : 1.08 / 1_000_000 ,
49+ } ,
50+ } ,
51+ 'moonshotai/kimi-k2.6' : {
52+ canopywaveId : 'moonshotai/kimi-k2.6' ,
53+ pricing : {
54+ inputCostPerToken : 0.60 / 1_000_000 ,
55+ cachedInputCostPerToken : 0.15 / 1_000_000 ,
56+ outputCostPerToken : 2.50 / 1_000_000 ,
57+ } ,
58+ } ,
3359}
3460
3561export function isCanopyWaveModel ( model : string ) : boolean {
36- return model in CANOPYWAVE_MODEL_MAP
62+ return model in CANOPYWAVE_MODELS
3763}
3864
3965function getCanopyWaveModelId ( openrouterModel : string ) : string {
40- return CANOPYWAVE_MODEL_MAP [ openrouterModel ] ?? openrouterModel
66+ return CANOPYWAVE_MODELS [ openrouterModel ] ?. canopywaveId ?? openrouterModel
67+ }
68+
69+ function getCanopyWavePricing ( model : string ) : CanopyWavePricing {
70+ const entry = CANOPYWAVE_MODELS [ model ]
71+ if ( ! entry ) {
72+ throw new Error ( `No CanopyWave pricing found for model: ${ model } ` )
73+ }
74+ return entry . pricing
4175}
4276
4377type StreamState = { responseText : string ; reasoningText : string ; ttftMs : number | null ; billedAlready : boolean }
@@ -86,30 +120,6 @@ function createCanopyWaveRequest(params: {
86120 } )
87121}
88122
89- // CanopyWave per-token pricing (dollars per token), keyed by OpenRouter model ID
90- interface CanopyWavePricing {
91- inputCostPerToken : number
92- cachedInputCostPerToken : number
93- outputCostPerToken : number
94- }
95-
96- const CANOPYWAVE_PRICING_MAP : Record < string , CanopyWavePricing > = {
97- 'minimax/minimax-m2.5' : {
98- inputCostPerToken : 0.27 / 1_000_000 ,
99- cachedInputCostPerToken : 0.03 / 1_000_000 ,
100- outputCostPerToken : 1.08 / 1_000_000 ,
101- } ,
102- 'moonshotai/kimi-k2.6' : {
103- inputCostPerToken : 0.60 / 1_000_000 ,
104- cachedInputCostPerToken : 0.15 / 1_000_000 ,
105- outputCostPerToken : 2.50 / 1_000_000 ,
106- } ,
107- }
108-
109- function getCanopyWavePricing ( model : string ) : CanopyWavePricing {
110- return CANOPYWAVE_PRICING_MAP [ model ] ?? CANOPYWAVE_PRICING_MAP [ 'moonshotai/kimi-k2.6' ]
111- }
112-
113123function extractUsageAndCost ( usage : Record < string , unknown > | undefined | null , model : string ) : UsageData {
114124 if ( ! usage ) return { inputTokens : 0 , outputTokens : 0 , cacheReadInputTokens : 0 , reasoningTokens : 0 , cost : 0 }
115125 const promptDetails = usage . prompt_tokens_details as Record < string , unknown > | undefined | null
0 commit comments