@@ -115,6 +115,21 @@ const API_STREAMS: Record<string, StreamFunction<Api, SimpleStreamOptions>> = {
115115
116116const ZERO_COST : ModelCost = { input : 0 , output : 0 , cacheRead : 0 , cacheWrite : 0 } ;
117117
118+ // Per-model Moonshot billing (USD per 1k tokens), reasoning capability, and input modalities.
119+ // Source: https://platform.moonshot.ai/docs/pricing/chat — verify against live page before shipping.
120+ // Used because Moonshot isn't in pi-ai's built-in catalog, so catalogModel() returns undefined
121+ // and we look up locally to avoid falling back to ZERO_COST (which would skew BYOK accounting).
122+ const MOONSHOT_MODEL_COSTS : Record < string , {
123+ cost : ModelCost ,
124+ reasoning : boolean ,
125+ input : ( "text" | "image" ) [ ] ,
126+ } > = {
127+ "kimi-k3" : { cost : { input : 0.002 , output : 0.008 , cacheRead : 0 , cacheWrite : 0 } , reasoning : true , input : [ "text" ] } ,
128+ "kimi-k2.7-code" : { cost : { input : 0.0015 , output : 0.006 , cacheRead : 0 , cacheWrite : 0 } , reasoning : true , input : [ "text" ] } ,
129+ "kimi-k2.7-code-highspeed" : { cost : { input : 0.0015 , output : 0.006 , cacheRead : 0 , cacheWrite : 0 } , reasoning : true , input : [ "text" ] } ,
130+ "kimi-k2.6" : { cost : { input : 0.0006 , output : 0.002 , cacheRead : 0 , cacheWrite : 0 } , reasoning : false , input : [ "text" ] } ,
131+ } ;
132+
118133// Consult pi's builtin catalog for cost/compat metadata of a known model id. Unknown models are
119134// fine (synthesized with zero cost). Import per-provider, not providers/all.
120135function catalogModel ( provider : AiModelConfig [ "provider" ] , modelId : string ) : Model < Api > | undefined {
@@ -124,6 +139,7 @@ function catalogModel(provider: AiModelConfig["provider"], modelId: string): Mod
124139 case "google" : return ( GOOGLE_MODELS as Record < string , Model < Api > > ) [ modelId ] ;
125140 case "cloudflare" : return ( CLOUDFLARE_WORKERS_AI_MODELS as Record < string , Model < Api > > ) [ modelId ] ;
126141 case "ollama" : return undefined ;
142+ case "moonshot" : return undefined ; // Moonshot is OpenAI-compatible; pi doesn't ship a catalog.
127143 default : return undefined ;
128144 }
129145}
@@ -231,6 +247,22 @@ function gatewayNativeModel(config: AiModelConfig, gatewayUrl: string): Model<Ap
231247 ...window ,
232248 compat : workersAiCompat ( catalog ) ,
233249 } ;
250+ case "moonshot" :
251+ // Moonshot is OpenAI-compatible; route through the gateway's /compat layer when the
252+ // gateway is configured. Currently inactive for this workshop (no moonshot provider in
253+ // the AI Gateway catalog), but kept symmetrical with `getModelDirect` so the direct
254+ // path mirrors the gateway-routed one.
255+ return {
256+ id : config . model ,
257+ name : catalog ?. name ?? config . model ,
258+ api : "openai-completions" ,
259+ provider : "moonshot" ,
260+ baseUrl : `${ gatewayUrl } /compat` ,
261+ reasoning : true ,
262+ input : [ "text" ] ,
263+ cost : catalog ?. cost ?? ZERO_COST ,
264+ ...window ,
265+ } ;
234266 default :
235267 return undefined ;
236268 }
@@ -587,6 +619,28 @@ function getModelDirect(config: AiModelConfig, sessionAffinity?: string): ModelH
587619 apiKey : config . apiToken ,
588620 sessionAffinity,
589621 } ) ;
622+ case "moonshot" : {
623+ // Direct Moonshot hit (no AI Gateway). Moonshot exposes an OpenAI-compatible
624+ // /v1/chat/completions endpoint, so we route through pi's openai-completions API impl.
625+ // Cost / reasoning / input are per-model: lookup locally so pi-ai's missing catalog
626+ // (catalogModel returns undefined) doesn't fall back to ZERO_COST.
627+ const spec = MOONSHOT_MODEL_COSTS [ config . model ] ;
628+ return makeHandle ( {
629+ model : {
630+ id : config . model ,
631+ name : catalog ?. name ?? config . model ,
632+ api : "openai-completions" ,
633+ provider : "moonshot" ,
634+ baseUrl : config . apiUrl ?? "https://api.moonshot.ai/v1" ,
635+ reasoning : spec ?. reasoning ?? true ,
636+ input : spec ?. input ?? [ "text" ] ,
637+ cost : spec ?. cost ?? ZERO_COST ,
638+ ...window ,
639+ } ,
640+ apiKey : config . apiToken ,
641+ sessionAffinity,
642+ } ) ;
643+ }
590644 default :
591645 config . provider satisfies never ;
592646 throw new Error ( `Unknown provider "${ config . provider } ".` ) ;
0 commit comments