From 46c9fe9fac4fdff92102b23d07d485b708d446bd Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Thu, 21 Aug 2025 13:31:15 +0000 Subject: [PATCH] Add pricing structure for token usage in CLI and enhance token handling in ModelDialog. Implement cost breakdown per model based on input, cached, and output tokens, and ensure proper initialization of token counts in usage handling. --- cli2.js | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-- modelDialog.js | 22 ++++++++++++----- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/cli2.js b/cli2.js index cf86f9a..4504646 100644 --- a/cli2.js +++ b/cli2.js @@ -16,8 +16,34 @@ modelDialog.on('reasoningUpdate', (output) => { //console.log(chalk.blue('reasoning event'),output); }); - - +// $ / 1million tokens +const price = { + 'gpt-5-2025-08-07': { + input: 1.25, + cached: 0.125, + output: 10 + }, + 'gpt-5-mini-2025-08-07': { + input: 0.25, + cached: 0.025, + output: 2 + }, + 'gpt-5-nano-2025-08-07': { + input: 0.05, + cached: 0.005, + output: 0.4 + }, + 'gpt-4.1-2025-04-14': { + input: 2, + cached: 0.5, + output: 8 + }, + 'gpt-4.1-mini-2025-04-14': { + input: 0.4, + cached: 0.1, + output: 1.6 + }, +}; (async ()=>{ @@ -27,5 +53,40 @@ modelDialog.on('reasoningUpdate', (output) => { const output2 = await modelDialog.interrogate('Hi, use the list files tools and the read files tool on /readme.txt in same variations to test it. use the tools in parallel.'); console.log('final output:',output2.output); console.log('reasoning:',output2.reasoning); + //Ti: { 'gpt-5-2025-08-07': 3019 } Tc: { 'gpt-5-2025-08-07': 0 } To: { 'gpt-5-2025-08-07': 751 } console.log('Ti:',output2.inputTokens,'Tc:',output2.cachedTokens,'To:',output2.outputTokens); + // cost breakdown per model and totals (prices are per 1M tokens) + const perMillion = 1_000_000; + const models = new Set([ + ...Object.keys(output2.inputTokens || {}), + ...Object.keys(output2.cachedTokens || {}), + ...Object.keys(output2.outputTokens || {}) + ]); + + let grandTotal = 0; + for (const model of models) { + const inputT = (output2.inputTokens || {})[model]; + const cachedT = (output2.cachedTokens || {})[model]; + const outputT = (output2.outputTokens || {})[model]; + + const p = price[model]; + const inputCost = (typeof inputT === 'number' && p) ? (inputT / perMillion) * p.input : undefined; + const cachedCost = (typeof cachedT === 'number' && p) ? (cachedT / perMillion) * p.cached : undefined; + const outputCost = (typeof outputT === 'number' && p) ? (outputT / perMillion) * p.output : undefined; + + const subtotal = [inputCost, cachedCost, outputCost].every(v => typeof v === 'number') + ? (inputCost + cachedCost + outputCost) + : undefined; + + if (typeof subtotal === 'number') grandTotal += subtotal; + + console.log('cost for', model, { + inputCost: parseFloat(inputCost.toFixed(6)), + cachedCost: parseFloat(cachedCost.toFixed(6)), + outputCost: parseFloat(outputCost.toFixed(6)), + subtotal: parseFloat(subtotal.toFixed(4)) + }); + } + + //console.log('total cost:', grandTotal); })() diff --git a/modelDialog.js b/modelDialog.js index 7b66756..06e291a 100644 --- a/modelDialog.js +++ b/modelDialog.js @@ -56,9 +56,12 @@ class ModelDialog { }; handleUsage = (usage, model) => { - this.inputTokens[model] = usage.input_tokens-usage.input_tokens_details.cached_tokens; - this.outputTokens[model] = usage.output_tokens; - this.cachedTokens[model] = usage.input_tokens_details.cached_tokens; + if (typeof this.inputTokens[model] !== 'number') this.inputTokens[model] = 0; + if (typeof this.outputTokens[model] !== 'number') this.outputTokens[model] = 0; + if (typeof this.cachedTokens[model] !== 'number') this.cachedTokens[model] = 0; + this.inputTokens[model] += usage.input_tokens - usage.input_tokens_details.cached_tokens; + this.outputTokens[model] += usage.output_tokens; + this.cachedTokens[model] += usage.input_tokens_details.cached_tokens; } on = (event, callback) => { @@ -89,16 +92,22 @@ class ModelDialog { //console.log(chalk.blue('messages:'),JSON.stringify(messagesToSend,null,2)); this.messagesSent.push(...messagesToSend); + const model = 'gpt-5-mini'; + const call = { - model: 'gpt-5', + model: model, input: messagesToSend, - text: { format: { type: 'text' }, verbosity: 'low' }, - reasoning: { effort: 'low', summary: 'detailed' }, + text: { format: { type: 'text' } }, tools: Object.values(toolsByFile).map(t => t.def), store: true, previous_response_id: this.previousResponseId, parallel_tool_calls: true } + if(model.startsWith('gpt-5')){ + call.reasoning = { effort: 'low', summary: 'detailed' }; + //call.text.format.verbosity = 'low'; + } + this.currentStream = openai.responses.stream(call); this.currentStream.on('response.created', (event) => { @@ -130,6 +139,7 @@ class ModelDialog { this.currentStream.on('response.completed', async (event) => { + //console.log(chalk.blue('response completed:'),event.response.usage); this.handleUsage(event.response.usage, event.response.model); outputs.push(...event.response.output);