mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
- Added logging for dynamic max tokens based on model details. - Increased max token limit for Claude model from 8000 to 128000. - Included beta header for Anthropik API call.
201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
import { convertToCoreMessages, streamText as _streamText, type Message } from 'ai';
|
|
import { MAX_TOKENS, type FileMap } from './constants';
|
|
import { getSystemPrompt } from '~/lib/common/prompts/prompts';
|
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER, MODIFICATIONS_TAG_NAME, PROVIDER_LIST, WORK_DIR } from '~/utils/constants';
|
|
import type { IProviderSetting } from '~/types/model';
|
|
import { PromptLibrary } from '~/lib/common/prompt-library';
|
|
import { allowedHTMLElements } from '~/utils/markdown';
|
|
import { LLMManager } from '~/lib/modules/llm/manager';
|
|
import { createScopedLogger } from '~/utils/logger';
|
|
import { createFilesContext, extractPropertiesFromMessage } from './utils';
|
|
|
|
export type Messages = Message[];
|
|
|
|
export interface StreamingOptions extends Omit<Parameters<typeof _streamText>[0], 'model'> {
|
|
supabaseConnection?: {
|
|
isConnected: boolean;
|
|
hasSelectedProject: boolean;
|
|
credentials?: {
|
|
anonKey?: string;
|
|
supabaseUrl?: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
const logger = createScopedLogger('stream-text');
|
|
|
|
export async function streamText(props: {
|
|
messages: Omit<Message, 'id'>[];
|
|
env?: Env;
|
|
options?: StreamingOptions;
|
|
apiKeys?: Record<string, string>;
|
|
files?: FileMap;
|
|
providerSettings?: Record<string, IProviderSetting>;
|
|
promptId?: string;
|
|
contextOptimization?: boolean;
|
|
contextFiles?: FileMap;
|
|
summary?: string;
|
|
messageSliceId?: number;
|
|
}) {
|
|
const {
|
|
messages,
|
|
env: serverEnv,
|
|
options,
|
|
apiKeys,
|
|
files,
|
|
providerSettings,
|
|
promptId,
|
|
contextOptimization,
|
|
contextFiles,
|
|
summary,
|
|
} = props;
|
|
let currentModel = DEFAULT_MODEL;
|
|
let currentProvider = DEFAULT_PROVIDER.name;
|
|
let processedMessages = messages.map((message) => {
|
|
if (message.role === 'user') {
|
|
const { model, provider, content } = extractPropertiesFromMessage(message);
|
|
currentModel = model;
|
|
currentProvider = provider;
|
|
|
|
return { ...message, content };
|
|
} else if (message.role == 'assistant') {
|
|
let content = message.content;
|
|
content = content.replace(/<div class=\\"__boltThought__\\">.*?<\/div>/s, '');
|
|
content = content.replace(/<think>.*?<\/think>/s, '');
|
|
|
|
// Remove package-lock.json content specifically keeping token usage MUCH lower
|
|
content = content.replace(
|
|
/<boltAction type="file" filePath="package-lock\.json">[\s\S]*?<\/boltAction>/g,
|
|
'[package-lock.json content removed]',
|
|
);
|
|
|
|
// Trim whitespace potentially left after removals
|
|
content = content.trim();
|
|
|
|
return { ...message, content };
|
|
}
|
|
|
|
return message;
|
|
});
|
|
|
|
const provider = PROVIDER_LIST.find((p) => p.name === currentProvider) || DEFAULT_PROVIDER;
|
|
const staticModels = LLMManager.getInstance().getStaticModelListFromProvider(provider);
|
|
let modelDetails = staticModels.find((m) => m.name === currentModel);
|
|
|
|
if (!modelDetails) {
|
|
const modelsList = [
|
|
...(provider.staticModels || []),
|
|
...(await LLMManager.getInstance().getModelListFromProvider(provider, {
|
|
apiKeys,
|
|
providerSettings,
|
|
serverEnv: serverEnv as any,
|
|
})),
|
|
];
|
|
|
|
if (!modelsList.length) {
|
|
throw new Error(`No models found for provider ${provider.name}`);
|
|
}
|
|
|
|
modelDetails = modelsList.find((m) => m.name === currentModel);
|
|
|
|
if (!modelDetails) {
|
|
// Fallback to first model
|
|
logger.warn(
|
|
`MODEL [${currentModel}] not found in provider [${provider.name}]. Falling back to first model. ${modelsList[0].name}`,
|
|
);
|
|
modelDetails = modelsList[0];
|
|
}
|
|
}
|
|
|
|
const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS;
|
|
logger.info(
|
|
`Max tokens for model ${modelDetails.name} is ${dynamicMaxTokens} based on ${modelDetails.maxTokenAllowed} or ${MAX_TOKENS}`,
|
|
);
|
|
|
|
let systemPrompt =
|
|
PromptLibrary.getPropmtFromLibrary(promptId || 'default', {
|
|
cwd: WORK_DIR,
|
|
allowedHtmlElements: allowedHTMLElements,
|
|
modificationTagName: MODIFICATIONS_TAG_NAME,
|
|
supabase: {
|
|
isConnected: options?.supabaseConnection?.isConnected || false,
|
|
hasSelectedProject: options?.supabaseConnection?.hasSelectedProject || false,
|
|
credentials: options?.supabaseConnection?.credentials || undefined,
|
|
},
|
|
}) ?? getSystemPrompt();
|
|
|
|
if (contextFiles && contextOptimization) {
|
|
const codeContext = createFilesContext(contextFiles, true);
|
|
|
|
systemPrompt = `${systemPrompt}
|
|
|
|
Below is the artifact containing the context loaded into context buffer for you to have knowledge of and might need changes to fullfill current user request.
|
|
CONTEXT BUFFER:
|
|
---
|
|
${codeContext}
|
|
---
|
|
`;
|
|
|
|
if (summary) {
|
|
systemPrompt = `${systemPrompt}
|
|
below is the chat history till now
|
|
CHAT SUMMARY:
|
|
---
|
|
${props.summary}
|
|
---
|
|
`;
|
|
|
|
if (props.messageSliceId) {
|
|
processedMessages = processedMessages.slice(props.messageSliceId);
|
|
} else {
|
|
const lastMessage = processedMessages.pop();
|
|
|
|
if (lastMessage) {
|
|
processedMessages = [lastMessage];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const effectiveLockedFilePaths = new Set<string>();
|
|
|
|
if (files) {
|
|
for (const [filePath, fileDetails] of Object.entries(files)) {
|
|
if (fileDetails?.isLocked) {
|
|
effectiveLockedFilePaths.add(filePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (effectiveLockedFilePaths.size > 0) {
|
|
const lockedFilesListString = Array.from(effectiveLockedFilePaths)
|
|
.map((filePath) => `- ${filePath}`)
|
|
.join('\n');
|
|
systemPrompt = `${systemPrompt}
|
|
|
|
IMPORTANT: The following files are locked and MUST NOT be modified in any way. Do not suggest or make any changes to these files. You can proceed with the request but DO NOT make any changes to these files specifically:
|
|
${lockedFilesListString}
|
|
---
|
|
`;
|
|
} else {
|
|
console.log('No locked files found from any source for prompt.');
|
|
}
|
|
|
|
logger.info(`Sending llm call to ${provider.name} with model ${modelDetails.name}`);
|
|
|
|
// console.log(systemPrompt, processedMessages);
|
|
|
|
return await _streamText({
|
|
model: provider.getModelInstance({
|
|
model: modelDetails.name,
|
|
serverEnv,
|
|
apiKeys,
|
|
providerSettings,
|
|
}),
|
|
system: systemPrompt,
|
|
maxTokens: dynamicMaxTokens,
|
|
messages: convertToCoreMessages(processedMessages as any),
|
|
...options,
|
|
});
|
|
}
|