(params: {
body: ChatCompletionRequestBody
originalModel: string
fetch: typeof globalThis.fetch
})
| 86 | } |
| 87 | |
| 88 | function createCanopyWaveRequest(params: { |
| 89 | body: ChatCompletionRequestBody |
| 90 | originalModel: string |
| 91 | fetch: typeof globalThis.fetch |
| 92 | }) { |
| 93 | const { body, originalModel, fetch } = params |
| 94 | const providerBody = isKimiModel(originalModel) |
| 95 | ? addKimiToolCompatibilityFields(body) |
| 96 | : body |
| 97 | const canopywaveBody: Record<string, unknown> = { |
| 98 | ...providerBody, |
| 99 | model: getCanopyWaveModelId(originalModel), |
| 100 | } |
| 101 | |
| 102 | // Strip OpenRouter-specific / internal fields |
| 103 | delete canopywaveBody.provider |
| 104 | delete canopywaveBody.transforms |
| 105 | delete canopywaveBody.codebuff_metadata |
| 106 | delete canopywaveBody.usage |
| 107 | |
| 108 | // For streaming, request usage in the final chunk |
| 109 | if (canopywaveBody.stream) { |
| 110 | canopywaveBody.stream_options = { include_usage: true } |
| 111 | } |
| 112 | |
| 113 | if (!env.CANOPYWAVE_API_KEY) { |
| 114 | throw new Error('CANOPYWAVE_API_KEY is not configured') |
| 115 | } |
| 116 | |
| 117 | return fetch(`${CANOPYWAVE_BASE_URL}/chat/completions`, { |
| 118 | method: 'POST', |
| 119 | headers: { |
| 120 | Authorization: `Bearer ${env.CANOPYWAVE_API_KEY}`, |
| 121 | 'Content-Type': 'application/json', |
| 122 | }, |
| 123 | body: JSON.stringify(canopywaveBody), |
| 124 | // @ts-expect-error - dispatcher is a valid undici option not in fetch types |
| 125 | dispatcher: canopywaveAgent, |
| 126 | }) |
| 127 | } |
| 128 | |
| 129 | function extractUsageAndCost(usage: Record<string, unknown> | undefined | null, model: string): UsageData { |
| 130 | if (!usage) return { inputTokens: 0, outputTokens: 0, cacheReadInputTokens: 0, reasoningTokens: 0, cost: 0 } |
no test coverage detected