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.
This commit is contained in:
65
cli2.js
65
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);
|
||||
})()
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user