Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@smythos/sre",
"version": "1.5.44",
"version": "1.5.45",
"description": "Smyth Runtime Environment",
"author": "Alaa-eddine KADDOURI",
"license": "MIT",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ export class OpenAIConnector extends LLMConnector {
// #region Validate token limit
const messages = _body?.messages || [];
const lastMessage = messages[messages.length - 1];

const promptTokens = context?.hasFiles
? await LLMHelper.countVisionPromptTokens(lastMessage?.content)
: encodeChat(messages as any, 'gpt-4')?.length;
const promptTokens = await this.computePromptTokens(messages, context);

await this.validateTokenLimit({
acRequest,
Expand Down Expand Up @@ -145,10 +142,7 @@ export class OpenAIConnector extends LLMConnector {
// #region Validate token limit
const messages = body?.messages || body?.input || [];
const lastMessage = messages[messages.length - 1];

const promptTokens = context?.hasFiles
? await LLMHelper.countVisionPromptTokens(lastMessage?.content)
: encodeChat(messages as any, 'gpt-4')?.length;
const promptTokens = await this.computePromptTokens(messages, context);

await this.validateTokenLimit({
acRequest,
Expand Down Expand Up @@ -302,6 +296,45 @@ export class OpenAIConnector extends LLMConnector {
return modelsProvider;
}

/**
* Safely compute prompt token count across different interfaces (Chat Completions, Responses)
* - Normalizes message content to strings for encodeChat
* - Handles vision prompts when files are present
* - Never throws; defaults to 0 on failure
*/
private async computePromptTokens(messages: any[], context: ILLMRequestContext): Promise<number> {
try {
if (context?.hasFiles) {
const lastMessage = messages?.[messages?.length - 1] || {};
const lastContent = lastMessage?.content ?? '';
return await LLMHelper.countVisionPromptTokens(lastContent || '');
}

const normalized = (messages || [])
.map((m) => {
if (!m || !m.role) return null;
let content = '';
if (Array.isArray(m.content)) {
content = m.content.map((b) => (typeof b?.text === 'string' ? b.text : '')).join(' ');
} else if (typeof m.content === 'string') {
content = m.content;
} else if (m.content !== undefined && m.content !== null) {
try {
content = JSON.stringify(m.content);
} catch (_) {
content = '';
}
}
return { role: m.role, content };
})
.filter(Boolean);

return encodeChat(normalized as any, 'gpt-4')?.length || 0;
} catch (_) {
return 0;
}
}

/**
* Prepare request body for OpenAI Responses API
* Uses MessageTransformer and ToolsTransformer for clean interface transformations
Expand Down
Loading