human (Nov 2, 2024, 11:05 AM)
curl https://api.anthropic.com/v1/messages/count_tokens \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "content-type: application/json" \
--header "anthropic-version: 2023-06-01" \
--header "anthropic-beta: token-counting-2024-11-01" \
--data '{
"model": "claude-3-5-sonnet-20241022",
"system": "You are a scientist",
"messages": [{
"role": "user",
"content": "Hello, Claude"
}]
}'
Build an artifact that lets me pipe content to this API and see the JSON results - pull the API key from ANTHROPIC_API_KEY
key in localStorage and prompt()
for it if it doesn't exist
assistant (Nov 2, 2024, 11:05 AM)
I'll help you create a simple HTML interface to interact with the token counting API.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Token Counter</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, Arial, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
textarea {
width: 100%;
min-height: 200px;
margin: 10px 0;
padding: 10px;
font-size: 16px;
font-family: monospace;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background: #0066ff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.output {
background: #f5f5f5;
padding: 20px;
border-radius: 4px;
margin-top: 20px;
white-space: pre-wrap;
font-family: monospace;
}
.error {
color: #ff0000;
margin: 10px 0;
}
.input-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Claude Token Counter</h1>
<div class="input-group">
<label for="system">System prompt:</label>
<textarea id="system" placeholder="Enter system prompt (optional)"></textarea>
</div>
<div class="input-group">
<label for="content">User message:</label>
<textarea id="content" placeholder="Enter message content"></textarea>
</div>
<button id="count">Count Tokens</button>
<div id="error" class="error"></div>
<div id="output" class="output"></div>
<script type="module">
const API_URL = 'https://api.anthropic.com/v1/messages/count_tokens'
const MODEL = 'claude-3-5-sonnet-20241022'
function getApiKey() {
let key = localStorage.getItem('ANTHROPIC_API_KEY')
if (!key) {
key = prompt('Please enter your Anthropic API key:')
if (key) {
localStorage.setItem('ANTHROPIC_API_KEY', key)
}
}
return key
}
async function countTokens(system, content) {
const apiKey = getApiKey()
if (!apiKey) {
throw new Error('API key is required')
}
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'content-type': 'application/json',
'anthropic-version': '2023-06-01',
'anthropic-beta': 'token-counting-2024-11-01'
},
body: JSON.stringify({
model: MODEL,
system: system || undefined,
messages: [{
role: 'user',
content: content
}]
})
})
if (!response.ok) {
const error = await response.text()
throw new Error(`API error: ${error}`)
}
return response.json()
}
const systemInput = document.getElementById('system')
const contentInput = document.getElementById('content')
const countButton = document.getElementById('count')
const errorDiv = document.getElementById('error')
const outputDiv = document.getElementById('output')
countButton.addEventListener('click', async () => {
errorDiv.textContent = ''
outputDiv.textContent = 'Counting tokens...'
countButton.disabled = true
try {
const result = await countTokens(
systemInput.value.trim(),
contentInput.value.trim()
)
outputDiv.textContent = JSON.stringify(result, null, 2)
} catch (error) {
errorDiv.textContent = error.message
outputDiv.textContent = ''
} finally {
countButton.disabled = false
}
})
</script>
</body>
</html>
I've created a simple token counting interface that:
- Provides separate inputs for system prompt and user message
- Stores the API key in localStorage and prompts for it if not found
- Makes API calls to the token counting endpoint
- Displays the full JSON response or any errors
- Uses vanilla JavaScript and CSS with no external dependencies
- Follows the requested style guidelines for CSS and JavaScript
To use it:
- Enter your text in either/both fields
- Click "Count Tokens"
- The first time, you'll be prompted for your API key
- Results will appear below in a formatted JSON display
Let me know if you'd like any adjustments to the styling or functionality!
human (Nov 2, 2024, 11:11 AM)
I fixed it to be like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Token Counter</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, Arial, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
textarea {
width: 100%;
min-height: 200px;
margin: 10px 0;
padding: 10px;
font-size: 16px;
font-family: monospace;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background: #0066ff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.output {
background: #f5f5f5;
padding: 20px;
border-radius: 4px;
margin-top: 20px;
white-space: pre-wrap;
font-family: monospace;
}
.error {
color: #ff0000;
margin: 10px 0;
}
.input-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Claude Token Counter</h1>
<div class="input-group">
<label for="system">System prompt:</label>
<textarea id="system" placeholder="Enter system prompt (optional)"></textarea>
</div>
<div class="input-group">
<label for="content">User message:</label>
<textarea id="content" placeholder="Enter message content"></textarea>
</div>
<button id="count">Count Tokens</button>
<div id="error" class="error"></div>
<div id="output" class="output"></div>
<script type="module">
const API_URL = 'https://api.anthropic.com/v1/messages/count_tokens'
const MODEL = 'claude-3-5-sonnet-20241022'
function getApiKey() {
let key = localStorage.getItem('ANTHROPIC_API_KEY')
if (!key) {
key = prompt('Please enter your Anthropic API key:')
if (key) {
localStorage.setItem('ANTHROPIC_API_KEY', key)
}
}
return key
}
async function countTokens(system, content) {
const apiKey = getApiKey()
if (!apiKey) {
throw new Error('API key is required')
}
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'content-type': 'application/json',
'anthropic-version': '2023-06-01',
'anthropic-beta': 'token-counting-2024-11-01',
'anthropic-dangerous-direct-browser-access': 'true'
},
body: JSON.stringify({
model: MODEL,
system: system || undefined,
messages: [{
role: 'user',
content: content
}]
})
})
if (!response.ok) {
const error = await response.text()
throw new Error(`API error: ${error}`)
}
return response.json()
}
const systemInput = document.getElementById('system')
const contentInput = document.getElementById('content')
const countButton = document.getElementById('count')
const errorDiv = document.getElementById('error')
const outputDiv = document.getElementById('output')
countButton.addEventListener('click', async () => {
errorDiv.textContent = ''
outputDiv.textContent = 'Counting tokens...'
countButton.disabled = true
try {
const result = await countTokens(
systemInput.value.trim(),
contentInput.value.trim()
)
outputDiv.textContent = JSON.stringify(result, null, 2)
} catch (error) {
errorDiv.textContent = error.message
outputDiv.textContent = ''
} finally {
countButton.disabled = false
}
})
</script>
</body>
</html>
Now update that to add a select box for picking the model from this list:
claude-3-opus-20240229 claude-3-sonnet-20240229 claude-3-haiku-20240307 claude-3-5-sonnet-20240620 claude-3-5-sonnet-20241022
Make the claude-3-5-sonnet-20241022
one the default
Also make the system prompt box smaller in height
assistant (Nov 2, 2024, 11:11 AM)
I'll update the HTML to add the model selector and adjust the system prompt height.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Token Counter</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Helvetica, Arial, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
textarea {
width: 100%;
min-height: 200px;
margin: 10px 0;
padding: 10px;
font-size: 16px;
font-family: monospace;
border: 1px solid #ccc;
border-radius: 4px;
}
textarea#system {
min-height: 100px;
}
select {
width: 100%;
padding: 8px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 10px 0;
}
button {
background: #0066ff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.output {
background: #f5f5f5;
padding: 20px;
border-radius: 4px;
margin-top: 20px;
white-space: pre-wrap;
font-family: monospace;
}
.error {
color: #ff0000;
margin: 10px 0;
}
.input-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Claude Token Counter</h1>
<div class="input-group">
<label for="model">Model:</label>
<select id="model">
<option value="claude-3-opus-20240229">Claude 3 Opus</option>
<option value="claude-3-sonnet-20240229">Claude 3 Sonnet</option>
<option value="claude-3-haiku-20240307">Claude 3 Haiku</option>
<option value="claude-3-5-sonnet-20240620">Claude 3.5 Sonnet (June)</option>
<option value="claude-3-5-sonnet-20241022" selected>Claude 3.5 Sonnet (Current)</option>
</select>
</div>
<div class="input-group">
<label for="system">System prompt:</label>
<textarea id="system" placeholder="Enter system prompt (optional)"></textarea>
</div>
<div class="input-group">
<label for="content">User message:</label>
<textarea id="content" placeholder="Enter message content"></textarea>
</div>
<button id="count">Count Tokens</button>
<div id="error" class="error"></div>
<div id="output" class="output"></div>
<script type="module">
const API_URL = 'https://api.anthropic.com/v1/messages/count_tokens'
function getApiKey() {
let key = localStorage.getItem('ANTHROPIC_API_KEY')
if (!key) {
key = prompt('Please enter your Anthropic API key:')
if (key) {
localStorage.setItem('ANTHROPIC_API_KEY', key)
}
}
return key
}
async function countTokens(system, content, model) {
const apiKey = getApiKey()
if (!apiKey) {
throw new Error('API key is required')
}
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'x-api-key': apiKey,
'content-type': 'application/json',
'anthropic-version': '2023-06-01',
'anthropic-beta': 'token-counting-2024-11-01',
'anthropic-dangerous-direct-browser-access': 'true'
},
body: JSON.stringify({
model: model,
system: system || undefined,
messages: [{
role: 'user',
content: content
}]
})
})
if (!response.ok) {
const error = await response.text()
throw new Error(`API error: ${error}`)
}
return response.json()
}
const modelSelect = document.getElementById('model')
const systemInput = document.getElementById('system')
const contentInput = document.getElementById('content')
const countButton = document.getElementById('count')
const errorDiv = document.getElementById('error')
const outputDiv = document.getElementById('output')
countButton.addEventListener('click', async () => {
errorDiv.textContent = ''
outputDiv.textContent = 'Counting tokens...'
countButton.disabled = true
try {
const result = await countTokens(
systemInput.value.trim(),
contentInput.value.trim(),
modelSelect.value
)
outputDiv.textContent = JSON.stringify(result, null, 2)
} catch (error) {
errorDiv.textContent = error.message
outputDiv.textContent = ''
} finally {
countButton.disabled = false
}
})
</script>
</body>
</html>
I've made the following changes:
- Added a model selector dropdown with all the specified models
- Set
claude-3-5-sonnet-20241022
as the default selected option - Reduced the system prompt textarea height to 100px using a specific CSS rule
- Updated the
countTokens
function to accept the model parameter - Passed the selected model value to the API call
The interface now allows users to select different Claude models while maintaining all the previous functionality. Let me know if you'd like any adjustments to the styling or behavior!