@smythos/sre 1.7.40 → 1.7.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +49 -42
- package/dist/index.js.map +1 -1
- package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
- package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
- package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/helpers/Conversation.helper.d.ts +10 -13
- package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
- package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
- package/dist/types/types/LLM.types.d.ts +23 -0
- package/package.json +1 -1
- package/src/Components/AgentPlugin.class.ts +20 -3
- package/src/Components/Classifier.class.ts +79 -16
- package/src/Components/ForEach.class.ts +34 -6
- package/src/Components/GenAILLM.class.ts +54 -23
- package/src/Components/LLMAssistant.class.ts +56 -21
- package/src/Components/RAG/DataSourceCleaner.class.ts +13 -11
- package/src/Components/RAG/DataSourceComponent.class.ts +39 -13
- package/src/Components/RAG/DataSourceIndexer.class.ts +18 -12
- package/src/Components/RAG/DataSourceLookup.class.ts +14 -10
- package/src/Components/ScrapflyWebScrape.class.ts +7 -0
- package/src/config.ts +1 -0
- package/src/helpers/Conversation.helper.ts +112 -26
- package/src/helpers/TemplateString.helper.ts +6 -5
- package/src/index.ts +1 -0
- package/src/index.ts.bak +1 -0
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +11 -0
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +9 -11
- package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +190 -146
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
- package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +229 -12
- package/src/types/LLM.types.ts +24 -0
- package/src/utils/data.utils.ts +6 -4
- package/src/Components/DataSourceIndexer.class.ts +0 -295
|
@@ -4,7 +4,7 @@ import { Logger } from '@sre/helpers/Log.helper';
|
|
|
4
4
|
import { LLMInference } from '@sre/LLMManager/LLM.inference';
|
|
5
5
|
import { LLMContext } from '@sre/MemoryManager/LLMContext';
|
|
6
6
|
import { TAgentProcessParams } from '@sre/types/Agent.types';
|
|
7
|
-
import { ILLMContextStore, TLLMEvent, TLLMModel, ToolData } from '@sre/types/LLM.types';
|
|
7
|
+
import { IConversationSettings, ILLMContextStore, TLLMEvent, TLLMModel, ToolData } from '@sre/types/LLM.types';
|
|
8
8
|
import { isUrl } from '@sre/utils/data.utils';
|
|
9
9
|
import { processWithConcurrencyLimit, uid } from '@sre/utils/general.utils';
|
|
10
10
|
import axios, { AxiosRequestConfig } from 'axios';
|
|
@@ -76,10 +76,24 @@ export class Conversation extends EventEmitter {
|
|
|
76
76
|
return this._id;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// Tool call limit tracking
|
|
80
|
+
private _toolCallCount: number = 0;
|
|
81
|
+
private _maxToolCallsPerSession: number = Infinity; // Default limit
|
|
82
|
+
private _disableToolsForNextCall: boolean = false;
|
|
83
|
+
|
|
79
84
|
public get context() {
|
|
80
85
|
return this._context;
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
public get storeId() {
|
|
89
|
+
return this._llmContextStore?.id;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Headers to be added to all tool call requests
|
|
94
|
+
*/
|
|
95
|
+
public headers: Record<string, string> = {};
|
|
96
|
+
|
|
83
97
|
private _lastError;
|
|
84
98
|
private _spec;
|
|
85
99
|
private _customToolsDeclarations: FunctionDeclaration[] = [];
|
|
@@ -122,22 +136,7 @@ export class Conversation extends EventEmitter {
|
|
|
122
136
|
return this._llmInference;
|
|
123
137
|
}
|
|
124
138
|
|
|
125
|
-
constructor(
|
|
126
|
-
private _model: string | TLLMModel,
|
|
127
|
-
private _specSource?: string | Record<string, any>,
|
|
128
|
-
private _settings?: {
|
|
129
|
-
maxContextSize?: number;
|
|
130
|
-
maxOutputTokens?: number;
|
|
131
|
-
systemPrompt?: string;
|
|
132
|
-
toolChoice?: string;
|
|
133
|
-
store?: ILLMContextStore;
|
|
134
|
-
experimentalCache?: boolean;
|
|
135
|
-
toolsStrategy?: (toolsConfig) => any;
|
|
136
|
-
agentId?: string;
|
|
137
|
-
agentVersion?: string;
|
|
138
|
-
baseUrl?: string;
|
|
139
|
-
}
|
|
140
|
-
) {
|
|
139
|
+
constructor(private _model: string | TLLMModel, private _specSource?: string | Record<string, any>, private _settings?: IConversationSettings) {
|
|
141
140
|
//TODO: handle loading previous session (messages)
|
|
142
141
|
super();
|
|
143
142
|
|
|
@@ -166,6 +165,10 @@ export class Conversation extends EventEmitter {
|
|
|
166
165
|
this._llmContextStore = _settings.store;
|
|
167
166
|
}
|
|
168
167
|
|
|
168
|
+
if (_settings?.maxToolCalls !== undefined) {
|
|
169
|
+
this._maxToolCallsPerSession = _settings.maxToolCalls;
|
|
170
|
+
}
|
|
171
|
+
|
|
169
172
|
this._baseUrl = _settings?.baseUrl;
|
|
170
173
|
|
|
171
174
|
this._agentVersion = _settings?.agentVersion;
|
|
@@ -305,6 +308,9 @@ export class Conversation extends EventEmitter {
|
|
|
305
308
|
const baseUrl = this._baseUrl;
|
|
306
309
|
const message_id = 'msg_' + randomUUID();
|
|
307
310
|
const isDebugSession = toolHeaders['X-DEBUG'];
|
|
311
|
+
for (let [key, value] of Object.entries(this.headers)) {
|
|
312
|
+
toolHeaders[key] = value;
|
|
313
|
+
}
|
|
308
314
|
|
|
309
315
|
/* ==================== STEP ENTRY ==================== */
|
|
310
316
|
// console.debug('Request to LLM with the given model, messages and functions properties.', {
|
|
@@ -333,13 +339,17 @@ export class Conversation extends EventEmitter {
|
|
|
333
339
|
requestId: llmReqUid,
|
|
334
340
|
});
|
|
335
341
|
|
|
342
|
+
// Disable tools if we've reached the limit (for final synthesis call)
|
|
343
|
+
const effectiveToolsConfig = this._disableToolsForNextCall ? null : toolsConfig;
|
|
344
|
+
this._disableToolsForNextCall = false; // Reset flag after using it
|
|
345
|
+
|
|
336
346
|
const eventEmitter: any = await this.llmInference
|
|
337
347
|
.promptStream({
|
|
338
348
|
contextWindow,
|
|
339
349
|
files,
|
|
340
350
|
params: {
|
|
341
351
|
model: this.model,
|
|
342
|
-
toolsConfig: this._settings?.toolsStrategy ? this._settings.toolsStrategy(
|
|
352
|
+
toolsConfig: this._settings?.toolsStrategy ? this._settings.toolsStrategy(effectiveToolsConfig) : effectiveToolsConfig,
|
|
343
353
|
maxTokens,
|
|
344
354
|
cache: this._settings?.experimentalCache,
|
|
345
355
|
agentId: this._agentId,
|
|
@@ -431,13 +441,44 @@ export class Conversation extends EventEmitter {
|
|
|
431
441
|
llmMessage.thinkingBlocks = thinkingBlocks;
|
|
432
442
|
}
|
|
433
443
|
|
|
444
|
+
// Check if we're at or over the tool call limit BEFORE processing this batch
|
|
445
|
+
const remainingToolCalls = this._maxToolCallsPerSession - this._toolCallCount;
|
|
446
|
+
|
|
447
|
+
if (remainingToolCalls <= 0) {
|
|
448
|
+
// Already at limit, don't execute any tools from this batch - all will be pending
|
|
449
|
+
const pendingToolNames = toolsData.map((t: ToolData) => t.name).join(', ');
|
|
450
|
+
const systemInstruction = `You have reached the maximum number of tool calls (${this._maxToolCallsPerSession}). The following tools were requested but marked as "pending": ${pendingToolNames}. Please provide a helpful response based on the information you've gathered so far. You may acknowledge these pending tools and suggest the user can continue in a follow-up request.`;
|
|
451
|
+
this._context.addUserMessage(systemInstruction, message_id, { internal: true });
|
|
452
|
+
this.emit(TLLMEvent.Interrupted, 'max_tool_calls', { requestId: llmReqUid });
|
|
453
|
+
this._disableToolsForNextCall = true;
|
|
454
|
+
|
|
455
|
+
// Continue to get final synthesis without executing tools
|
|
456
|
+
this.streamPrompt(null, toolHeaders, concurrentToolCalls, abortSignal).then(resolve).catch(reject);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// If this batch would exceed the limit, truncate to only execute remaining quota
|
|
461
|
+
let actualToolsData = toolsData;
|
|
462
|
+
let skippedToolsData: ToolData[] = [];
|
|
463
|
+
|
|
464
|
+
if (toolsData.length > remainingToolCalls) {
|
|
465
|
+
actualToolsData = toolsData.slice(0, remainingToolCalls);
|
|
466
|
+
skippedToolsData = toolsData.slice(remainingToolCalls);
|
|
467
|
+
|
|
468
|
+
const skippedToolNames = skippedToolsData.map((t) => t.name).join(', ');
|
|
469
|
+
console.warn(
|
|
470
|
+
`Tool call limit will be reached. Executing only ${remainingToolCalls} of ${toolsData.length} requested tools. ` +
|
|
471
|
+
`Skipped tools: ${skippedToolNames}`
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
434
475
|
//add tool status for every tool entry
|
|
435
|
-
|
|
476
|
+
actualToolsData.forEach((tool) => {
|
|
436
477
|
tool.status = tool.name ? this._toolStatusMap?.[tool.name] : undefined;
|
|
437
478
|
});
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
479
|
+
actualToolsData.content = _content;
|
|
480
|
+
actualToolsData.requestId = llmReqUid;
|
|
481
|
+
actualToolsData.contextWindow = contextWindow;
|
|
441
482
|
|
|
442
483
|
llmMessage.tool_calls = toolsData.map((tool) => {
|
|
443
484
|
return {
|
|
@@ -452,7 +493,8 @@ export class Conversation extends EventEmitter {
|
|
|
452
493
|
|
|
453
494
|
//if (llmMessage.tool_calls?.length <= 0) return;
|
|
454
495
|
|
|
455
|
-
|
|
496
|
+
// Emit ToolInfo with only the tools we'll actually execute
|
|
497
|
+
this.emit(TLLMEvent.ToolInfo, actualToolsData);
|
|
456
498
|
|
|
457
499
|
//initialize the agent callback logic
|
|
458
500
|
const _agentCallback = (data) => {
|
|
@@ -487,7 +529,8 @@ export class Conversation extends EventEmitter {
|
|
|
487
529
|
//eventEmitter.emit('content', data);
|
|
488
530
|
};
|
|
489
531
|
|
|
490
|
-
|
|
532
|
+
// Only process tools up to the limit
|
|
533
|
+
const toolProcessingTasks = actualToolsData.map(
|
|
491
534
|
(tool: { index: number; name: string; type: string; arguments: Record<string, any> }) => async () => {
|
|
492
535
|
const endpoint = endpoints?.get(tool?.name) || tool?.name;
|
|
493
536
|
// Sometimes we have object response from the LLM such as Anthropic
|
|
@@ -532,22 +575,47 @@ export class Conversation extends EventEmitter {
|
|
|
532
575
|
this.emit('afterToolCall', { tool, args }, functionResponse); // Deprecated
|
|
533
576
|
this.emit(TLLMEvent.ToolResult, { tool, result, requestId: llmReqUid });
|
|
534
577
|
|
|
578
|
+
// Increment tool call counter
|
|
579
|
+
this._toolCallCount++;
|
|
580
|
+
|
|
535
581
|
return { ...tool, result: functionResponse };
|
|
536
582
|
}
|
|
537
583
|
);
|
|
538
584
|
|
|
539
585
|
const processedToolsData = await processWithConcurrencyLimit<ToolData>(toolProcessingTasks, concurrentToolCalls);
|
|
540
586
|
|
|
587
|
+
// Add skipped tools with pending status (not errors - they can be executed in next request)
|
|
588
|
+
const skippedToolsWithPendingStatus = skippedToolsData.map((tool) => ({
|
|
589
|
+
...tool,
|
|
590
|
+
result: JSON.stringify({
|
|
591
|
+
status: 'pending',
|
|
592
|
+
message: `Tool execution deferred - maximum tool call limit (${this._maxToolCallsPerSession}) reached for this request. This tool can be executed in a follow-up request.`,
|
|
593
|
+
pending: true,
|
|
594
|
+
}),
|
|
595
|
+
}));
|
|
596
|
+
|
|
597
|
+
// Combine executed tools and pending tools for context
|
|
598
|
+
const allToolsData = [...processedToolsData, ...skippedToolsWithPendingStatus];
|
|
599
|
+
|
|
600
|
+
// Emit pending status for skipped tools (not errors - these are valid requests)
|
|
601
|
+
skippedToolsWithPendingStatus.forEach((tool) => {
|
|
602
|
+
this.emit(TLLMEvent.ToolResult, {
|
|
603
|
+
tool,
|
|
604
|
+
result: { status: 'pending', message: 'Tool execution deferred - limit reached', pending: true },
|
|
605
|
+
requestId: llmReqUid,
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
541
609
|
//if (!passThroughContent) {
|
|
542
610
|
|
|
543
611
|
if (!passThroughContent) {
|
|
544
|
-
this._context.addToolMessage(llmMessage,
|
|
612
|
+
this._context.addToolMessage(llmMessage, allToolsData, message_id);
|
|
545
613
|
//delete toolHeaders['x-passthrough'];
|
|
546
614
|
} else {
|
|
547
615
|
//this._context.addAssistantMessage(passThroughContent, message_id);
|
|
548
616
|
|
|
549
617
|
//llmMessage.content += '\n' + passThroughContent;
|
|
550
|
-
this._context.addToolMessage(llmMessage,
|
|
618
|
+
this._context.addToolMessage(llmMessage, allToolsData, message_id, { passThrough: true });
|
|
551
619
|
|
|
552
620
|
//this._context.addAssistantMessage(passThroughContent, message_id, { passthrough: true });
|
|
553
621
|
//this should not be stored in the persistent conversation store
|
|
@@ -556,6 +624,24 @@ export class Conversation extends EventEmitter {
|
|
|
556
624
|
//toolHeaders['x-passthrough'] = 'true';
|
|
557
625
|
}
|
|
558
626
|
|
|
627
|
+
// Check if tool call limit has been reached AFTER processing this batch
|
|
628
|
+
const limitReached = this._toolCallCount >= this._maxToolCallsPerSession;
|
|
629
|
+
const hasPendingTools = skippedToolsWithPendingStatus.length > 0;
|
|
630
|
+
|
|
631
|
+
if (limitReached) {
|
|
632
|
+
// Disable tools for the next (final) call to prevent infinite loops
|
|
633
|
+
this._disableToolsForNextCall = true;
|
|
634
|
+
|
|
635
|
+
if (hasPendingTools) {
|
|
636
|
+
// Only add system instruction if there are pending tools
|
|
637
|
+
// If no pending tools, LLM completed naturally - don't confuse it with limit messages
|
|
638
|
+
const systemInstruction = `You have reached the maximum number of tool calls (${this._maxToolCallsPerSession}) for this request. Some tools are marked as "pending" and were not executed. Please provide a helpful response based on the information you've gathered so far. You may acknowledge these pending tools and suggest the user can continue in a follow-up request.`;
|
|
639
|
+
|
|
640
|
+
this._context.addUserMessage(systemInstruction, message_id, { internal: true });
|
|
641
|
+
this.emit(TLLMEvent.Interrupted, 'max_tool_calls', { requestId: llmReqUid });
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
559
645
|
this.streamPrompt(null, toolHeaders, concurrentToolCalls, abortSignal).then(resolve).catch(reject);
|
|
560
646
|
|
|
561
647
|
//} else {
|
|
@@ -103,7 +103,7 @@ export class TemplateStringHelper {
|
|
|
103
103
|
* unmatched placeholders will be left as is
|
|
104
104
|
* Recursively resolves nested template variables until no more variables are found
|
|
105
105
|
*/
|
|
106
|
-
public parse(data: Record<string,
|
|
106
|
+
public parse(data: Record<string, unknown>, regex: TemplateStringMatch = Match.default, maxDepth: number = 5) {
|
|
107
107
|
if (typeof this._current !== 'string' || typeof data !== 'object') return this;
|
|
108
108
|
|
|
109
109
|
// Keep parsing until no more template variables are resolved or max depth is reached
|
|
@@ -114,12 +114,13 @@ export class TemplateStringHelper {
|
|
|
114
114
|
this._current = this._current.replace(regex, (match, token) => {
|
|
115
115
|
let val = data?.[token] ?? match; // Use nullish coalescing to preserve falsy values (0, '', false)
|
|
116
116
|
|
|
117
|
-
//if no exact match, try to parse the token as a JSON expression
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
// if no exact match, try to parse the token as a JSON expression
|
|
118
|
+
// * Nullish check: using `==` intentionally to match both null and undefined
|
|
119
|
+
if (data?.[token] == null) {
|
|
120
|
+
val = JSONExpression(data, token) ?? `{{${token}}}`; //if no match, use the token as is
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
return typeof val === 'object' ? JSON.stringify(val) : escapeJsonField(val);
|
|
123
|
+
return typeof val === 'object' ? JSON.stringify(val) : escapeJsonField(val as string);
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
// Break early if no changes were made : we parsed all the template variables
|
package/src/index.ts
CHANGED
|
@@ -93,6 +93,7 @@ export * from './Components/APICall/parseUrl';
|
|
|
93
93
|
export * from './Components/Image/imageSettings.config';
|
|
94
94
|
export * from './Components/RAG/DataSourceCleaner.class';
|
|
95
95
|
export * from './Components/RAG/DataSourceComponent.class';
|
|
96
|
+
export * from './Components/RAG/DataSourceIndexer.class';
|
|
96
97
|
export * from './Components/RAG/DataSourceLookup.class';
|
|
97
98
|
export * from './Components/Triggers/Gmail.trigger';
|
|
98
99
|
export * from './Components/Triggers/JobScheduler.trigger';
|
package/src/index.ts.bak
CHANGED
|
@@ -93,6 +93,7 @@ export * from './Components/APICall/parseUrl';
|
|
|
93
93
|
export * from './Components/Image/imageSettings.config';
|
|
94
94
|
export * from './Components/RAG/DataSourceCleaner.class';
|
|
95
95
|
export * from './Components/RAG/DataSourceComponent.class';
|
|
96
|
+
export * from './Components/RAG/DataSourceIndexer.class';
|
|
96
97
|
export * from './Components/RAG/DataSourceLookup.class';
|
|
97
98
|
export * from './Components/Triggers/Gmail.trigger';
|
|
98
99
|
export * from './Components/Triggers/JobScheduler.trigger';
|
|
@@ -35,6 +35,7 @@ export interface IVectorDBRequest {
|
|
|
35
35
|
|
|
36
36
|
export abstract class VectorDBConnector extends SecureConnector<IVectorDBRequest> {
|
|
37
37
|
protected readonly USER_METADATA_KEY = 'user_metadata';
|
|
38
|
+
protected readonly LEGACY_USER_METADATA_KEY = 'metadata';
|
|
38
39
|
|
|
39
40
|
public abstract id: string;
|
|
40
41
|
public abstract getResourceACL(resourceId: string, candidate: IAccessCandidate): Promise<ACL>;
|
|
@@ -201,10 +201,21 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
201
201
|
for (const match of results.matches) {
|
|
202
202
|
if (match.metadata?.isSkeletonVector) continue;
|
|
203
203
|
|
|
204
|
+
// priortize user metadata over the default flat metadata
|
|
204
205
|
if (match.metadata?.[this.USER_METADATA_KEY]) {
|
|
205
206
|
match.metadata[this.USER_METADATA_KEY] = JSONContentHelper.create(match.metadata[this.USER_METADATA_KEY].toString()).tryParse();
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
// if legacy metadata is present, we add it to the fallback metadata obj
|
|
210
|
+
if (match.metadata?.[this.LEGACY_USER_METADATA_KEY]) {
|
|
211
|
+
const parsedMetadata = JSONContentHelper.create(match.metadata[this.LEGACY_USER_METADATA_KEY].toString()).tryParse();
|
|
212
|
+
match.metadata = {
|
|
213
|
+
...match.metadata,
|
|
214
|
+
...parsedMetadata,
|
|
215
|
+
};
|
|
216
|
+
delete match.metadata?.[this.LEGACY_USER_METADATA_KEY];
|
|
217
|
+
}
|
|
218
|
+
|
|
208
219
|
const text = match.metadata?.text as string | undefined;
|
|
209
220
|
delete match.metadata?.text; // delete the text metadata to avoid duplication in case we returned the default raw metadata
|
|
210
221
|
|
|
@@ -40,16 +40,14 @@ export class EmbeddingsFactory {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
public static getModels() {
|
|
43
|
-
return Object.keys(supportedProviders)
|
|
44
|
-
.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}, [] as { provider: SupportedProviders; model: SupportedModels[SupportedProviders] }[])
|
|
53
|
-
.filter((item) => item.model !== 'text-embedding-ada-002'); //! SPECIAL case for ada-002, it doesn't support dimensions passing
|
|
43
|
+
return Object.keys(supportedProviders).reduce((acc, provider) => {
|
|
44
|
+
acc.push(
|
|
45
|
+
...supportedProviders[provider].models.map((model) => ({
|
|
46
|
+
provider,
|
|
47
|
+
model,
|
|
48
|
+
}))
|
|
49
|
+
);
|
|
50
|
+
return acc;
|
|
51
|
+
}, [] as { provider: SupportedProviders; model: SupportedModels[SupportedProviders] }[]);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
@@ -248,4 +248,29 @@ export class LLMHelper {
|
|
|
248
248
|
|
|
249
249
|
return _messages;
|
|
250
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Checks if the given model is part of the Claude 4 family.
|
|
254
|
+
*
|
|
255
|
+
* @param {string} modelId - The model identifier to check.
|
|
256
|
+
* @returns {boolean} True if the model is Claude 4 family, false otherwise.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* const isClaude4 = LLMHelper.isClaude4Family('claude-sonnet-4-20250514');
|
|
260
|
+
* console.log(isClaude4); // true
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* const isClaude4 = LLMHelper.isClaude4Family('claude-opus-4-5');
|
|
264
|
+
* console.log(isClaude4); // true
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* const isClaude4 = LLMHelper.isClaude4Family('gpt-4-turbo');
|
|
268
|
+
* console.log(isClaude4); // false
|
|
269
|
+
*/
|
|
270
|
+
public static isClaude4Family(modelId: string): boolean {
|
|
271
|
+
if (!modelId) return false;
|
|
272
|
+
// Match patterns like: claude-4-*, claude-{variant}-4-*, claude-{variant}-4
|
|
273
|
+
// Examples: claude-opus-4-5, claude-sonnet-4-20250514, claude-4-opus
|
|
274
|
+
return /claude-(?:\w+-)?4(?:-|$)/i.test(modelId);
|
|
275
|
+
}
|
|
251
276
|
}
|
|
@@ -275,7 +275,7 @@ export abstract class LLMConnector extends Connector {
|
|
|
275
275
|
const teamId = await this.getTeamId(candidate);
|
|
276
276
|
|
|
277
277
|
// We need the model entry name for usage reporting
|
|
278
|
-
_params.modelEntryName = typeof model === 'string' ? model :
|
|
278
|
+
_params.modelEntryName = typeof model === 'string' ? model : model?.modelEntryName || model?.modelId;
|
|
279
279
|
_params.teamId = teamId;
|
|
280
280
|
|
|
281
281
|
const modelProviderCandidate = modelsProvider.requester(candidate);
|