Skip to content

Commit a8a2a0d

Browse files
committed
feat(new tool): GPT Token Counter
Fix CorentinTh#1334
1 parent f1adc66 commit a8a2a0d

File tree

6 files changed

+209
-0
lines changed

6 files changed

+209
-0
lines changed

components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ declare module '@vue/runtime-core' {
257257
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
258258
NMenu: typeof import('naive-ui')['NMenu']
259259
NScrollbar: typeof import('naive-ui')['NScrollbar']
260+
NScrollbar: typeof import('naive-ui')['NScrollbar']
260261
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
261262
OcrImage: typeof import('./src/tools/ocr-image/ocr-image.vue')['default']
262263
Option43Generator: typeof import('./src/tools/option43-generator/option43-generator.vue')['default']

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
"file-type": "^19.6.0",
121121
"flatten-anything": "^4.0.1",
122122
"fuse.js": "^6.6.2",
123+
"gpt-tokens": "^1.3.13",
123124
"generate-schema": "^2.6.0",
124125
"get-timezone-offset": "^1.0.5",
125126
"hash-wasm": "^4.11.0",

pnpm-lock.yaml

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<script setup lang="ts">
2+
import { GPTTokens } from 'gpt-tokens';
3+
import JSON5 from 'json5';
4+
import type { supportModelType } from 'gpt-tokens';
5+
import TextareaCopyable from '@/components/TextareaCopyable.vue';
6+
import { useValidation } from '@/composable/validation';
7+
import { useQueryParamOrStorage } from '@/composable/queryParams';
8+
9+
const models = GPTTokens.supportModels;
10+
11+
const model = useQueryParamOrStorage({ name: 'model', storageName: 'gpt-counter:model', defaultValue: 'gpt-3.5-turbo-1106' });
12+
const systemPrompt = ref('');
13+
const userPrompt = ref('');
14+
15+
const isAdvancedMode = ref(false);
16+
const messagesJsonArray = ref('');
17+
const toolsJsonArray = ref('');
18+
19+
const messagesValidation = useValidation({
20+
source: messagesJsonArray,
21+
rules: [
22+
{
23+
message: 'Invalid "messages" array',
24+
validator: value => value && JSON5.parse(value.trim()),
25+
},
26+
],
27+
});
28+
const toolsValidation = useValidation({
29+
source: toolsJsonArray,
30+
rules: [
31+
{
32+
message: 'Invalid "tools" array',
33+
validator: value => value && JSON5.parse(value.trim()),
34+
},
35+
],
36+
});
37+
38+
const outputTokenCosts = computed(() => {
39+
try {
40+
let messagesArray = [];
41+
let toolsArray = [];
42+
43+
if (isAdvancedMode.value) {
44+
messagesArray = messagesJsonArray.value ? JSON5.parse(messagesJsonArray.value.trim()) : [];
45+
toolsArray = toolsJsonArray.value ? JSON5.parse(toolsJsonArray.value.trim()) : [];
46+
}
47+
else {
48+
if (systemPrompt.value) {
49+
messagesArray.push({ role: 'system', content: systemPrompt.value });
50+
}
51+
if (userPrompt.value) {
52+
messagesArray.push({ role: 'user', content: userPrompt.value });
53+
}
54+
}
55+
56+
if (!messagesArray.length) {
57+
return {
58+
error: '',
59+
usedTokens: '0',
60+
usedUSD: '0',
61+
promptUsedTokens: '0',
62+
completionUsedTokens: '0',
63+
};
64+
}
65+
66+
const tokens = new GPTTokens({
67+
model: model.value as supportModelType,
68+
messages: messagesArray,
69+
tools: toolsArray,
70+
});
71+
return {
72+
error: '',
73+
usedTokens: tokens.usedTokens.toString(),
74+
usedUSD: tokens.usedUSD.toString(),
75+
promptUsedTokens: tokens.promptUsedTokens.toString(),
76+
completionUsedTokens: tokens.completionUsedTokens.toString(),
77+
};
78+
}
79+
catch (e: any) {
80+
return {
81+
error: e.toString(),
82+
usedTokens: '',
83+
usedUSD: '',
84+
promptUsedTokens: '',
85+
completionUsedTokens: '',
86+
};
87+
}
88+
});
89+
</script>
90+
91+
<template>
92+
<div>
93+
<c-select
94+
v-model:value="model"
95+
label-position="left"
96+
label="Model:"
97+
:options="models"
98+
placeholder="Select GPT model"
99+
mb-2
100+
/>
101+
102+
<div flex justify-center>
103+
<n-form-item label="Advanded JSON Mode" label-placement="left">
104+
<n-checkbox v-model:checked="isAdvancedMode" mr-2 />
105+
</n-form-item>
106+
</div>
107+
108+
<c-card v-if="isAdvancedMode" title="Prompts">
109+
<c-input-text
110+
v-model:value="messagesJsonArray"
111+
multiline raw-text
112+
placeholder="Your 'messages' JSON array..."
113+
rows="5"
114+
autofocus
115+
label="Your 'messages' JSON array:"
116+
:validation="messagesValidation"
117+
/>
118+
119+
<c-input-text
120+
v-model:value="toolsJsonArray"
121+
multiline raw-text
122+
placeholder="Your 'tools' JSON array..."
123+
rows="5"
124+
autofocus
125+
label="Your 'tools' JSON array:"
126+
:validation="toolsValidation"
127+
/>
128+
</c-card>
129+
<c-card v-else title="Input JSON(s)">
130+
<c-input-text
131+
v-model:value="systemPrompt"
132+
multiline raw-text
133+
placeholder="Your System Prompt content..."
134+
rows="2"
135+
autofocus
136+
label="Your System Prompt content:"
137+
/>
138+
139+
<c-input-text
140+
v-model:value="userPrompt"
141+
multiline raw-text
142+
placeholder="Your User Prompt content..."
143+
rows="6"
144+
autofocus
145+
label="Your User Prompt content:"
146+
/>
147+
</c-card>
148+
149+
<n-divider />
150+
151+
<c-alert v-if="outputTokenCosts.error">
152+
{{ outputTokenCosts.error }}
153+
</c-alert>
154+
155+
<div v-if="!outputTokenCosts.error">
156+
<n-form-item label="Used Tokens:">
157+
<TextareaCopyable :value="outputTokenCosts.usedTokens" />
158+
</n-form-item>
159+
<n-form-item label="Prompt Tokens:">
160+
<TextareaCopyable :value="outputTokenCosts.promptUsedTokens" />
161+
</n-form-item>
162+
<n-form-item label="Completion Tokens:">
163+
<TextareaCopyable :value="outputTokenCosts.completionUsedTokens" />
164+
</n-form-item>
165+
<n-form-item label="Used USD:">
166+
<TextareaCopyable :value="outputTokenCosts.usedUSD" />
167+
</n-form-item>
168+
</div>
169+
</div>
170+
</template>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { CurrencyDollar } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'GPT Token Estimator',
6+
path: '/gpt-token-estimator',
7+
description: 'OpenAI GPT Token Estimator',
8+
keywords: ['gpt', 'token', 'estimator'],
9+
component: () => import('./gpt-token-estimator.vue'),
10+
icon: CurrencyDollar,
11+
createdAt: new Date('2024-08-15'),
12+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { tool as angleConverter } from './angle-converter';
2525
import { tool as floatingPointNumberConverter } from './floating-point-number-converter';
2626
import { tool as snowflakeIdExtractor } from './snowflake-id-extractor';
2727
import { tool as emailNormalizer } from './email-normalizer';
28+
import { tool as gptTokenEstimator } from './gpt-token-estimator';
2829
import { tool as myIp } from './my-ip';
2930
import { tool as geoDistanceCalculator } from './geo-distance-calculator';
3031
import { tool as xVrSpamcauseDecoder } from './x-vr-spamcause-decoder';
@@ -461,6 +462,7 @@ export const toolsByCategory: ToolCategory[] = [
461462
components: [
462463
gitMemo,
463464
nanoMemo,
465+
gptTokenEstimator,
464466
],
465467
},
466468
{

0 commit comments

Comments
 (0)