mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
Another attempt to add toek usage info
This commit is contained in:
parent
2e05270bab
commit
225b553876
@ -1 +1 @@
|
|||||||
{ "commit": "fcb61ba49990d1d0cc13d05a0958007b6e9c1c38" }
|
{ "commit": "2e05270bab264d7ee83d7a1dd5408c969f648a68" }
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { Markdown } from './Markdown';
|
import { Markdown } from './Markdown';
|
||||||
|
import { USAGE_REGEX } from '~/utils/constants';
|
||||||
|
|
||||||
interface AssistantMessageProps {
|
interface AssistantMessageProps {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AssistantMessage = memo(({ content }: AssistantMessageProps) => {
|
export const AssistantMessage = memo(({ content }: AssistantMessageProps) => {
|
||||||
|
const match = content.match(USAGE_REGEX);
|
||||||
|
const usage = match ? JSON.parse(match[1]) : null;
|
||||||
|
const cleanContent = content.replace(USAGE_REGEX, '').trim();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden w-full">
|
<div className="overflow-hidden w-full">
|
||||||
<Markdown html>{content}</Markdown>
|
{usage && (
|
||||||
|
<div className="text-sm text-bolt-elements-textSecondary mb-2">
|
||||||
|
Tokens: {usage.totalTokens} (prompt: {usage.promptTokens}, completion: {usage.completionTokens})
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Markdown html>{cleanContent}</Markdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -12,42 +12,36 @@ interface UserMessageProps {
|
|||||||
export function UserMessage({ content }: UserMessageProps) {
|
export function UserMessage({ content }: UserMessageProps) {
|
||||||
if (Array.isArray(content)) {
|
if (Array.isArray(content)) {
|
||||||
const textItem = content.find((item) => item.type === 'text');
|
const textItem = content.find((item) => item.type === 'text');
|
||||||
const textContent = sanitizeUserMessage(textItem?.text || '');
|
const textContent = stripMetadata(textItem?.text || '');
|
||||||
const images = content.filter((item) => item.type === 'image' && item.image);
|
const images = content.filter((item) => item.type === 'image' && item.image);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden pt-[4px]">
|
<div className="overflow-hidden pt-[4px]">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="flex-1">
|
{textContent && <Markdown html>{textContent}</Markdown>}
|
||||||
<Markdown limitedMarkdown>{textContent}</Markdown>
|
|
||||||
</div>
|
|
||||||
{images.length > 0 && (
|
|
||||||
<div className="flex-shrink-0 w-[160px]">
|
|
||||||
{images.map((item, index) => (
|
{images.map((item, index) => (
|
||||||
<div key={index} className="relative">
|
|
||||||
<img
|
<img
|
||||||
|
key={index}
|
||||||
src={item.image}
|
src={item.image}
|
||||||
alt={`Uploaded image ${index + 1}`}
|
alt={`Image ${index + 1}`}
|
||||||
className="w-full h-[160px] rounded-lg object-cover border border-bolt-elements-borderColor"
|
className="max-w-full h-auto rounded-lg"
|
||||||
|
style={{ maxHeight: '512px', objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const textContent = sanitizeUserMessage(content);
|
const textContent = stripMetadata(content);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden pt-[4px]">
|
<div className="overflow-hidden pt-[4px]">
|
||||||
<Markdown limitedMarkdown>{textContent}</Markdown>
|
<Markdown html>{textContent}</Markdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeUserMessage(content: string) {
|
function stripMetadata(content: string) {
|
||||||
return content.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '');
|
return content.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '');
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default class SwitchableStream extends TransformStream {
|
export default class SwitchableStream extends TransformStream {
|
||||||
private _controller: TransformStreamDefaultController | null = null;
|
_controller: TransformStreamDefaultController | null = null;
|
||||||
private _currentReader: ReadableStreamDefaultReader | null = null;
|
private _currentReader: ReadableStreamDefaultReader | null = null;
|
||||||
private _switches = 0;
|
private _switches = 0;
|
||||||
|
|
||||||
|
@ -41,12 +41,36 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
|
|||||||
|
|
||||||
const stream = new SwitchableStream();
|
const stream = new SwitchableStream();
|
||||||
|
|
||||||
|
const cumulativeUsage = {
|
||||||
|
completionTokens: 0,
|
||||||
|
promptTokens: 0,
|
||||||
|
totalTokens: 0,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options: StreamingOptions = {
|
const options: StreamingOptions = {
|
||||||
toolChoice: 'none',
|
toolChoice: 'none',
|
||||||
onFinish: async ({ text: content, finishReason, usage }) => {
|
onFinish: async ({ text: content, finishReason, usage }) => {
|
||||||
console.log('usage', usage);
|
console.log('usage', usage);
|
||||||
|
|
||||||
|
if (usage && stream._controller) {
|
||||||
|
cumulativeUsage.completionTokens += usage.completionTokens || 0;
|
||||||
|
cumulativeUsage.promptTokens += usage.promptTokens || 0;
|
||||||
|
cumulativeUsage.totalTokens += usage.totalTokens || 0;
|
||||||
|
|
||||||
|
// Send usage info in message metadata for assistant messages
|
||||||
|
const usageMetadata = `0:"[Usage: ${JSON.stringify({
|
||||||
|
completionTokens: cumulativeUsage.completionTokens,
|
||||||
|
promptTokens: cumulativeUsage.promptTokens,
|
||||||
|
totalTokens: cumulativeUsage.totalTokens,
|
||||||
|
})}\n]"`;
|
||||||
|
|
||||||
|
console.log(usageMetadata);
|
||||||
|
|
||||||
|
const encodedData = new TextEncoder().encode(usageMetadata);
|
||||||
|
stream._controller.enqueue(encodedData);
|
||||||
|
}
|
||||||
|
|
||||||
if (finishReason !== 'length') {
|
if (finishReason !== 'length') {
|
||||||
return stream.close();
|
return stream.close();
|
||||||
}
|
}
|
||||||
@ -83,6 +107,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
|
|||||||
files,
|
files,
|
||||||
providerSettings,
|
providerSettings,
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.switchSource(result.toDataStream());
|
stream.switchSource(result.toDataStream());
|
||||||
|
|
||||||
return new Response(stream.readable, {
|
return new Response(stream.readable, {
|
||||||
|
@ -9,6 +9,7 @@ export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
|
|||||||
export const MODIFICATIONS_TAG_NAME = 'bolt_file_modifications';
|
export const MODIFICATIONS_TAG_NAME = 'bolt_file_modifications';
|
||||||
export const MODEL_REGEX = /^\[Model: (.*?)\]\n\n/;
|
export const MODEL_REGEX = /^\[Model: (.*?)\]\n\n/;
|
||||||
export const PROVIDER_REGEX = /\[Provider: (.*?)\]\n\n/;
|
export const PROVIDER_REGEX = /\[Provider: (.*?)\]\n\n/;
|
||||||
|
export const USAGE_REGEX = /\[Usage: ({.*?})\]/; // Keep this regex for assistant messages
|
||||||
export const DEFAULT_MODEL = 'claude-3-5-sonnet-latest';
|
export const DEFAULT_MODEL = 'claude-3-5-sonnet-latest';
|
||||||
export const PROMPT_COOKIE_KEY = 'cachedPrompt';
|
export const PROMPT_COOKIE_KEY = 'cachedPrompt';
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user