@smythos/sre 1.5.75 → 1.6.1
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/CHANGELOG +16 -10
- package/dist/index.js +42 -42
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Ollama.class.d.ts +45 -0
- package/dist/types/types/LLM.types.d.ts +2 -0
- package/package.json +2 -1
- package/src/Components/HuggingFace.class.ts +2 -2
- package/src/index.ts +1 -0
- package/src/index.ts.bak +1 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +362 -0
- package/src/subsystems/LLMManager/LLM.service/index.ts +3 -0
- package/src/subsystems/MemoryManager/Cache.service/connectors/RAMCache.class.ts +14 -1
- package/src/subsystems/MemoryManager/RuntimeContext.ts +1 -1
- package/src/types/LLM.types.ts +1 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -168,6 +168,7 @@ export * from './subsystems/LLMManager/LLM.service/connectors/Bedrock.class';
|
|
|
168
168
|
export * from './subsystems/LLMManager/LLM.service/connectors/Echo.class';
|
|
169
169
|
export * from './subsystems/LLMManager/LLM.service/connectors/GoogleAI.class';
|
|
170
170
|
export * from './subsystems/LLMManager/LLM.service/connectors/Groq.class';
|
|
171
|
+
export * from './subsystems/LLMManager/LLM.service/connectors/Ollama.class';
|
|
171
172
|
export * from './subsystems/LLMManager/LLM.service/connectors/Perplexity.class';
|
|
172
173
|
export * from './subsystems/LLMManager/LLM.service/connectors/VertexAI.class';
|
|
173
174
|
export * from './subsystems/LLMManager/LLM.service/connectors/xAI.class';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import { TLLMMessageBlock, ToolData, APIKeySource, ILLMRequestFuncParams, TLLMChatResponse, TLLMPreparedParams, TLLMToolResultMessageBlock, TLLMRequestBody } from '@sre/types/LLM.types';
|
|
3
|
+
import { LLMConnector } from '../LLMConnector';
|
|
4
|
+
export declare class OllamaConnector extends LLMConnector {
|
|
5
|
+
name: string;
|
|
6
|
+
private getClient;
|
|
7
|
+
protected request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse>;
|
|
8
|
+
protected streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter>;
|
|
9
|
+
protected reqBodyAdapter(params: TLLMPreparedParams): Promise<TLLMRequestBody>;
|
|
10
|
+
protected reportUsage(usage: {
|
|
11
|
+
prompt_tokens: number;
|
|
12
|
+
completion_tokens: number;
|
|
13
|
+
total_tokens: number;
|
|
14
|
+
}, metadata: {
|
|
15
|
+
modelEntryName: string;
|
|
16
|
+
keySource: APIKeySource;
|
|
17
|
+
agentId: string;
|
|
18
|
+
teamId: string;
|
|
19
|
+
}): {
|
|
20
|
+
sourceId: string;
|
|
21
|
+
input_tokens: number;
|
|
22
|
+
output_tokens: number;
|
|
23
|
+
input_tokens_cache_write: number;
|
|
24
|
+
input_tokens_cache_read: number;
|
|
25
|
+
keySource: APIKeySource;
|
|
26
|
+
agentId: string;
|
|
27
|
+
teamId: string;
|
|
28
|
+
};
|
|
29
|
+
transformToolMessageBlocks({ messageBlock, toolsData, }: {
|
|
30
|
+
messageBlock: TLLMMessageBlock;
|
|
31
|
+
toolsData: ToolData[];
|
|
32
|
+
}): TLLMToolResultMessageBlock[];
|
|
33
|
+
formatToolsConfig({ type, toolDefinitions, toolChoice }: {
|
|
34
|
+
type?: string;
|
|
35
|
+
toolDefinitions: any;
|
|
36
|
+
toolChoice?: string;
|
|
37
|
+
}): {
|
|
38
|
+
tools: any[];
|
|
39
|
+
tool_choice: string;
|
|
40
|
+
} | {
|
|
41
|
+
tools?: undefined;
|
|
42
|
+
tool_choice?: undefined;
|
|
43
|
+
};
|
|
44
|
+
getConsistentMessages(messages: TLLMMessageBlock[]): TLLMMessageBlock[];
|
|
45
|
+
}
|
|
@@ -228,6 +228,7 @@ export declare const BuiltinLLMProviders: {
|
|
|
228
228
|
readonly VertexAI: "VertexAI";
|
|
229
229
|
readonly xAI: "xAI";
|
|
230
230
|
readonly Perplexity: "Perplexity";
|
|
231
|
+
readonly Ollama: "Ollama";
|
|
231
232
|
};
|
|
232
233
|
export type TBuiltinLLMProvider = (typeof BuiltinLLMProviders)[keyof typeof BuiltinLLMProviders];
|
|
233
234
|
export interface ILLMProviders {
|
|
@@ -245,6 +246,7 @@ export declare const TLLMProvider: {
|
|
|
245
246
|
readonly VertexAI: "VertexAI";
|
|
246
247
|
readonly xAI: "xAI";
|
|
247
248
|
readonly Perplexity: "Perplexity";
|
|
249
|
+
readonly Ollama: "Ollama";
|
|
248
250
|
};
|
|
249
251
|
export type TBedrockSettings = {
|
|
250
252
|
keyIDName: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smythos/sre",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Smyth Runtime Environment",
|
|
5
5
|
"author": "Alaa-eddine KADDOURI",
|
|
6
6
|
"license": "MIT",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"mime": "^4.0.3",
|
|
83
83
|
"mysql2": "^3.11.3",
|
|
84
84
|
"oauth-1.0a": "^2.2.6",
|
|
85
|
+
"ollama": "^0.6.0",
|
|
85
86
|
"openai": "^5.12.2",
|
|
86
87
|
"p-limit": "^6.1.0",
|
|
87
88
|
"qs": "^6.13.0",
|
|
@@ -150,7 +150,7 @@ export class HuggingFace extends Component {
|
|
|
150
150
|
const binaryFile = BinaryInput.from(value, undefined, undefined, AccessCandidate.agent(agentId));
|
|
151
151
|
// const buffer = await binaryFile.readData(AccessCandidate.agent(agentId));
|
|
152
152
|
const buffer = await binaryFile.getBuffer();
|
|
153
|
-
const blob = new Blob([buffer]);
|
|
153
|
+
const blob = new Blob([buffer as any]);
|
|
154
154
|
inputs[name] = blob;
|
|
155
155
|
} catch (error: any) {
|
|
156
156
|
return { _error: error?.message || JSON.stringify(error), _debug: logger.output };
|
|
@@ -311,4 +311,4 @@ export class HuggingFace extends Component {
|
|
|
311
311
|
return { _error: `Error from Hugging Face: \n${error?.message || JSON.stringify(error)}`, _debug: logger.output };
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
|
-
}
|
|
314
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -174,6 +174,7 @@ export * from './subsystems/LLMManager/LLM.service/connectors/Bedrock.class';
|
|
|
174
174
|
export * from './subsystems/LLMManager/LLM.service/connectors/Echo.class';
|
|
175
175
|
export * from './subsystems/LLMManager/LLM.service/connectors/GoogleAI.class';
|
|
176
176
|
export * from './subsystems/LLMManager/LLM.service/connectors/Groq.class';
|
|
177
|
+
export * from './subsystems/LLMManager/LLM.service/connectors/Ollama.class';
|
|
177
178
|
export * from './subsystems/LLMManager/LLM.service/connectors/Perplexity.class';
|
|
178
179
|
export * from './subsystems/LLMManager/LLM.service/connectors/VertexAI.class';
|
|
179
180
|
export * from './subsystems/LLMManager/LLM.service/connectors/xAI.class';
|
package/src/index.ts.bak
CHANGED
|
@@ -174,6 +174,7 @@ export * from './subsystems/LLMManager/LLM.service/connectors/Bedrock.class';
|
|
|
174
174
|
export * from './subsystems/LLMManager/LLM.service/connectors/Echo.class';
|
|
175
175
|
export * from './subsystems/LLMManager/LLM.service/connectors/GoogleAI.class';
|
|
176
176
|
export * from './subsystems/LLMManager/LLM.service/connectors/Groq.class';
|
|
177
|
+
export * from './subsystems/LLMManager/LLM.service/connectors/Ollama.class';
|
|
177
178
|
export * from './subsystems/LLMManager/LLM.service/connectors/Perplexity.class';
|
|
178
179
|
export * from './subsystems/LLMManager/LLM.service/connectors/VertexAI.class';
|
|
179
180
|
export * from './subsystems/LLMManager/LLM.service/connectors/xAI.class';
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { Ollama, ChatResponse } from 'ollama';
|
|
2
|
+
import EventEmitter from 'events';
|
|
3
|
+
|
|
4
|
+
import { JSON_RESPONSE_INSTRUCTION, BUILT_IN_MODEL_PREFIX } from '@sre/constants';
|
|
5
|
+
import {
|
|
6
|
+
TLLMMessageBlock,
|
|
7
|
+
ToolData,
|
|
8
|
+
TLLMMessageRole,
|
|
9
|
+
APIKeySource,
|
|
10
|
+
TLLMEvent,
|
|
11
|
+
ILLMRequestFuncParams,
|
|
12
|
+
TLLMChatResponse,
|
|
13
|
+
ILLMRequestContext,
|
|
14
|
+
TLLMPreparedParams,
|
|
15
|
+
TLLMToolResultMessageBlock,
|
|
16
|
+
TLLMRequestBody,
|
|
17
|
+
} from '@sre/types/LLM.types';
|
|
18
|
+
import { LLMHelper } from '@sre/LLMManager/LLM.helper';
|
|
19
|
+
|
|
20
|
+
import { LLMConnector } from '../LLMConnector';
|
|
21
|
+
import { SystemEvents } from '@sre/Core/SystemEvents';
|
|
22
|
+
import { Logger } from '@sre/helpers/Log.helper';
|
|
23
|
+
|
|
24
|
+
const logger = Logger('OllamaConnector');
|
|
25
|
+
|
|
26
|
+
type OllamaChatRequest = {
|
|
27
|
+
model: string;
|
|
28
|
+
messages: any[];
|
|
29
|
+
stream?: boolean;
|
|
30
|
+
options?: {
|
|
31
|
+
num_predict?: number;
|
|
32
|
+
temperature?: number;
|
|
33
|
+
top_p?: number;
|
|
34
|
+
top_k?: number;
|
|
35
|
+
stop?: string[];
|
|
36
|
+
};
|
|
37
|
+
tools?: any[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export class OllamaConnector extends LLMConnector {
|
|
41
|
+
public name = 'LLM:Ollama';
|
|
42
|
+
|
|
43
|
+
private getClient(context: ILLMRequestContext): Ollama {
|
|
44
|
+
// Extract baseURL and sanitize it for Ollama SDK
|
|
45
|
+
let host = 'http://localhost:11434';
|
|
46
|
+
|
|
47
|
+
if (context.modelInfo.baseURL) {
|
|
48
|
+
// Handle baseURL that might include /api/ suffix
|
|
49
|
+
const baseURL = context.modelInfo.baseURL;
|
|
50
|
+
if (baseURL.endsWith('/api/')) {
|
|
51
|
+
// Remove /api/ suffix to get the root host
|
|
52
|
+
host = baseURL.replace(/\/api\/$/, '');
|
|
53
|
+
} else if (baseURL.endsWith('/api')) {
|
|
54
|
+
// Remove /api suffix
|
|
55
|
+
host = baseURL.replace(/\/api$/, '');
|
|
56
|
+
} else {
|
|
57
|
+
host = baseURL;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// No API key validation required for Ollama (local by default)
|
|
62
|
+
return new Ollama({ host });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
|
|
66
|
+
try {
|
|
67
|
+
logger.debug(`request ${this.name}`, acRequest.candidate);
|
|
68
|
+
const ollama = this.getClient(context);
|
|
69
|
+
|
|
70
|
+
const result = await ollama.chat({
|
|
71
|
+
...body,
|
|
72
|
+
stream: false,
|
|
73
|
+
}) as unknown as ChatResponse;
|
|
74
|
+
|
|
75
|
+
const message = result.message;
|
|
76
|
+
const finishReason = result.done_reason || 'stop';
|
|
77
|
+
const usage = {
|
|
78
|
+
prompt_tokens: result.prompt_eval_count || 0,
|
|
79
|
+
completion_tokens: result.eval_count || 0,
|
|
80
|
+
total_tokens: (result.prompt_eval_count || 0) + (result.eval_count || 0),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this.reportUsage(usage, {
|
|
84
|
+
modelEntryName: context.modelEntryName,
|
|
85
|
+
keySource: context.isUserKey ? APIKeySource.User : APIKeySource.Smyth,
|
|
86
|
+
agentId: context.agentId,
|
|
87
|
+
teamId: context.teamId,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
let toolsData: ToolData[] = [];
|
|
91
|
+
let useTool = false;
|
|
92
|
+
|
|
93
|
+
// Handle tool calls if present
|
|
94
|
+
if (message?.tool_calls) {
|
|
95
|
+
toolsData = message.tool_calls.map((tool, index) => ({
|
|
96
|
+
index,
|
|
97
|
+
id: tool.function?.name || `tool_${index}`,
|
|
98
|
+
type: 'function',
|
|
99
|
+
name: tool.function.name,
|
|
100
|
+
arguments: tool.function.arguments,
|
|
101
|
+
role: TLLMMessageRole.Assistant,
|
|
102
|
+
}));
|
|
103
|
+
useTool = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
content: message?.content ?? '',
|
|
108
|
+
finishReason,
|
|
109
|
+
useTool,
|
|
110
|
+
toolsData,
|
|
111
|
+
message: message as any,
|
|
112
|
+
usage,
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger.error(`request ${this.name}`, error, acRequest.candidate);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
|
|
121
|
+
try {
|
|
122
|
+
logger.debug(`streamRequest ${this.name}`, acRequest.candidate);
|
|
123
|
+
const emitter = new EventEmitter();
|
|
124
|
+
const usage_data = [];
|
|
125
|
+
|
|
126
|
+
const ollama = this.getClient(context);
|
|
127
|
+
const stream = await ollama.chat({
|
|
128
|
+
...body,
|
|
129
|
+
stream: true,
|
|
130
|
+
}) as AsyncIterable<ChatResponse>;
|
|
131
|
+
|
|
132
|
+
let toolsData: ToolData[] = [];
|
|
133
|
+
let fullContent = '';
|
|
134
|
+
|
|
135
|
+
(async () => {
|
|
136
|
+
for await (const chunk of stream) {
|
|
137
|
+
// Emit content deltas
|
|
138
|
+
if (chunk.message?.content) {
|
|
139
|
+
const content = chunk.message.content;
|
|
140
|
+
fullContent += content;
|
|
141
|
+
emitter.emit('content', content);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle tool calls accumulation
|
|
145
|
+
if (chunk.message?.tool_calls) {
|
|
146
|
+
chunk.message.tool_calls.forEach((toolCall, index) => {
|
|
147
|
+
if (!toolsData[index]) {
|
|
148
|
+
toolsData[index] = {
|
|
149
|
+
index,
|
|
150
|
+
id: toolCall.function?.name || `tool_${index}`,
|
|
151
|
+
type: 'function',
|
|
152
|
+
name: toolCall.function?.name,
|
|
153
|
+
arguments: toolCall.function?.arguments || '',
|
|
154
|
+
role: 'assistant',
|
|
155
|
+
};
|
|
156
|
+
} else {
|
|
157
|
+
// Merge arguments across chunks for string arguments
|
|
158
|
+
if (typeof toolsData[index].arguments === 'string' && typeof toolCall.function?.arguments === 'string') {
|
|
159
|
+
toolsData[index].arguments += toolCall.function.arguments;
|
|
160
|
+
} else {
|
|
161
|
+
// For object arguments, merge them properly
|
|
162
|
+
toolsData[index].arguments = { ...toolsData[index].arguments as any, ...toolCall.function?.arguments };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Capture usage data when available
|
|
169
|
+
if (chunk.prompt_eval_count !== undefined || chunk.eval_count !== undefined) {
|
|
170
|
+
const usage = {
|
|
171
|
+
prompt_tokens: chunk.prompt_eval_count || 0,
|
|
172
|
+
completion_tokens: chunk.eval_count || 0,
|
|
173
|
+
total_tokens: (chunk.prompt_eval_count || 0) + (chunk.eval_count || 0),
|
|
174
|
+
};
|
|
175
|
+
usage_data.push(usage);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Emit tool info if tools were requested
|
|
180
|
+
if (toolsData.length > 0) {
|
|
181
|
+
emitter.emit(TLLMEvent.ToolInfo, toolsData);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Report usage
|
|
185
|
+
usage_data.forEach((usage) => {
|
|
186
|
+
this.reportUsage(usage, {
|
|
187
|
+
modelEntryName: context.modelEntryName,
|
|
188
|
+
keySource: context.isUserKey ? APIKeySource.User : APIKeySource.Smyth,
|
|
189
|
+
agentId: context.agentId,
|
|
190
|
+
teamId: context.teamId,
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Final end event
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
emitter.emit('end', toolsData);
|
|
197
|
+
}, 100);
|
|
198
|
+
})();
|
|
199
|
+
|
|
200
|
+
return emitter;
|
|
201
|
+
} catch (error: any) {
|
|
202
|
+
logger.error(`streamRequest ${this.name}`, error, acRequest.candidate);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
protected async reqBodyAdapter(params: TLLMPreparedParams): Promise<TLLMRequestBody> {
|
|
208
|
+
const messages = params?.messages || [];
|
|
209
|
+
|
|
210
|
+
const body: OllamaChatRequest = {
|
|
211
|
+
model: params.model as string,
|
|
212
|
+
messages,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Handle JSON response format
|
|
216
|
+
const responseFormat = params?.responseFormat || '';
|
|
217
|
+
if (responseFormat === 'json') {
|
|
218
|
+
if (messages?.[0]?.role === 'system') {
|
|
219
|
+
messages[0].content += JSON_RESPONSE_INSTRUCTION;
|
|
220
|
+
} else {
|
|
221
|
+
messages.unshift({ role: 'system', content: JSON_RESPONSE_INSTRUCTION });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Map SRE options to Ollama options
|
|
226
|
+
const options: any = {};
|
|
227
|
+
|
|
228
|
+
if (params.maxTokens !== undefined) options.num_predict = params.maxTokens;
|
|
229
|
+
if (params.temperature !== undefined) options.temperature = params.temperature;
|
|
230
|
+
if (params.topP !== undefined) options.top_p = params.topP;
|
|
231
|
+
if (params.topK !== undefined) options.top_k = params.topK;
|
|
232
|
+
if (params.stopSequences?.length) options.stop = params.stopSequences;
|
|
233
|
+
|
|
234
|
+
if (Object.keys(options).length > 0) {
|
|
235
|
+
body.options = options;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Handle tools
|
|
239
|
+
if (params.toolsConfig?.tools) {
|
|
240
|
+
body.tools = params.toolsConfig.tools.map(tool => ({
|
|
241
|
+
type: 'function',
|
|
242
|
+
function: {
|
|
243
|
+
name: tool.function.name,
|
|
244
|
+
description: tool.function.description,
|
|
245
|
+
parameters: tool.function.parameters,
|
|
246
|
+
},
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return body as unknown as TLLMRequestBody;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
protected reportUsage(
|
|
254
|
+
usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number },
|
|
255
|
+
metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }
|
|
256
|
+
) {
|
|
257
|
+
// SmythOS (built-in) models have a prefix, so we need to remove it to get the model name
|
|
258
|
+
const modelName = metadata.modelEntryName.replace(BUILT_IN_MODEL_PREFIX, '');
|
|
259
|
+
|
|
260
|
+
const usageData = {
|
|
261
|
+
sourceId: `llm:${modelName}`,
|
|
262
|
+
input_tokens: usage.prompt_tokens,
|
|
263
|
+
output_tokens: usage.completion_tokens,
|
|
264
|
+
input_tokens_cache_write: 0,
|
|
265
|
+
input_tokens_cache_read: 0,
|
|
266
|
+
keySource: metadata.keySource,
|
|
267
|
+
agentId: metadata.agentId,
|
|
268
|
+
teamId: metadata.teamId,
|
|
269
|
+
};
|
|
270
|
+
SystemEvents.emit('USAGE:LLM', usageData);
|
|
271
|
+
|
|
272
|
+
return usageData;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public transformToolMessageBlocks({
|
|
276
|
+
messageBlock,
|
|
277
|
+
toolsData,
|
|
278
|
+
}: {
|
|
279
|
+
messageBlock: TLLMMessageBlock;
|
|
280
|
+
toolsData: ToolData[];
|
|
281
|
+
}): TLLMToolResultMessageBlock[] {
|
|
282
|
+
const messageBlocks: TLLMToolResultMessageBlock[] = [];
|
|
283
|
+
|
|
284
|
+
// Transform the assistant message block if present
|
|
285
|
+
if (messageBlock) {
|
|
286
|
+
const transformedMessageBlock = {
|
|
287
|
+
...messageBlock,
|
|
288
|
+
content: typeof messageBlock.content === 'object' ? JSON.stringify(messageBlock.content) : messageBlock.content,
|
|
289
|
+
};
|
|
290
|
+
if (transformedMessageBlock.tool_calls) {
|
|
291
|
+
for (let toolCall of transformedMessageBlock.tool_calls) {
|
|
292
|
+
const args = toolCall?.function?.arguments;
|
|
293
|
+
if (typeof args === 'string') {
|
|
294
|
+
try {
|
|
295
|
+
toolCall.function.arguments = JSON.parse(args);
|
|
296
|
+
} catch {
|
|
297
|
+
toolCall.function.arguments = {};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// If it's already an object, keep as-is for Ollama
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
messageBlocks.push(transformedMessageBlock);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Transform tool results into tool role messages
|
|
307
|
+
const transformedToolsData = toolsData.map((toolData) => ({
|
|
308
|
+
tool_call_id: toolData.id,
|
|
309
|
+
role: TLLMMessageRole.Tool,
|
|
310
|
+
name: toolData.name,
|
|
311
|
+
content: typeof toolData.result === 'string' ? toolData.result : JSON.stringify(toolData.result),
|
|
312
|
+
}));
|
|
313
|
+
|
|
314
|
+
return [...messageBlocks, ...transformedToolsData];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
public formatToolsConfig({ type = 'function', toolDefinitions, toolChoice = 'auto' }) {
|
|
318
|
+
let tools = [];
|
|
319
|
+
|
|
320
|
+
if (type === 'function') {
|
|
321
|
+
tools = toolDefinitions.map((tool) => {
|
|
322
|
+
const { name, description, properties, requiredFields } = tool;
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
type: 'function',
|
|
326
|
+
function: {
|
|
327
|
+
name,
|
|
328
|
+
description,
|
|
329
|
+
parameters: {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties,
|
|
332
|
+
required: requiredFields,
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return tools?.length > 0 ? { tools, tool_choice: toolChoice } : {};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public getConsistentMessages(messages: TLLMMessageBlock[]): TLLMMessageBlock[] {
|
|
343
|
+
const _messages = LLMHelper.removeDuplicateUserMessages(messages);
|
|
344
|
+
|
|
345
|
+
return _messages.map((message) => {
|
|
346
|
+
const _message = { ...message };
|
|
347
|
+
let textContent = '';
|
|
348
|
+
|
|
349
|
+
if (message?.parts) {
|
|
350
|
+
textContent = message.parts.map((textBlock) => textBlock?.text || '').join(' ');
|
|
351
|
+
} else if (Array.isArray(message?.content)) {
|
|
352
|
+
textContent = message.content.map((textBlock) => textBlock?.text || '').join(' ');
|
|
353
|
+
} else if (message?.content) {
|
|
354
|
+
textContent = message.content as string;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
_message.content = textContent;
|
|
358
|
+
|
|
359
|
+
return _message;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -11,6 +11,7 @@ import { BedrockConnector } from './connectors/Bedrock.class';
|
|
|
11
11
|
import { VertexAIConnector } from './connectors/VertexAI.class';
|
|
12
12
|
import { PerplexityConnector } from './connectors/Perplexity.class';
|
|
13
13
|
import { xAIConnector } from './connectors/xAI.class';
|
|
14
|
+
import { OllamaConnector } from './connectors/Ollama.class';
|
|
14
15
|
|
|
15
16
|
export class LLMService extends ConnectorServiceProvider {
|
|
16
17
|
public register() {
|
|
@@ -25,6 +26,7 @@ export class LLMService extends ConnectorServiceProvider {
|
|
|
25
26
|
ConnectorService.register(TConnectorService.LLM, 'VertexAI', VertexAIConnector);
|
|
26
27
|
ConnectorService.register(TConnectorService.LLM, 'xAI', xAIConnector);
|
|
27
28
|
ConnectorService.register(TConnectorService.LLM, 'Perplexity', PerplexityConnector);
|
|
29
|
+
ConnectorService.register(TConnectorService.LLM, 'Ollama', OllamaConnector);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
public init() {
|
|
@@ -40,5 +42,6 @@ export class LLMService extends ConnectorServiceProvider {
|
|
|
40
42
|
ConnectorService.init(TConnectorService.LLM, 'VertexAI');
|
|
41
43
|
ConnectorService.init(TConnectorService.LLM, 'xAI');
|
|
42
44
|
ConnectorService.init(TConnectorService.LLM, 'Perplexity');
|
|
45
|
+
ConnectorService.init(TConnectorService.LLM, 'Ollama');
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -129,7 +129,20 @@ export class RAMCache extends CacheConnector {
|
|
|
129
129
|
const entry = this.cache.get(fullMetadataKey);
|
|
130
130
|
|
|
131
131
|
if (entry) {
|
|
132
|
-
entry.metadata
|
|
132
|
+
const existingMetadata = entry.metadata || {};
|
|
133
|
+
const existingAcl = existingMetadata?.acl;
|
|
134
|
+
const mergedMetadata: CacheMetadata = { ...existingMetadata, ...metadata } as CacheMetadata;
|
|
135
|
+
|
|
136
|
+
// Preserve or establish ACL; always ensure requester retains ownership
|
|
137
|
+
if (existingAcl) {
|
|
138
|
+
mergedMetadata.acl = ACL.from(existingAcl).addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner).ACL;
|
|
139
|
+
} else if (mergedMetadata.acl) {
|
|
140
|
+
mergedMetadata.acl = ACL.from(mergedMetadata.acl).addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner).ACL;
|
|
141
|
+
} else {
|
|
142
|
+
mergedMetadata.acl = new ACL().addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner).ACL;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
entry.metadata = mergedMetadata;
|
|
133
146
|
this.cache.set(fullMetadataKey, entry);
|
|
134
147
|
}
|
|
135
148
|
}
|
package/src/types/LLM.types.ts
CHANGED
|
@@ -268,6 +268,7 @@ export const BuiltinLLMProviders = {
|
|
|
268
268
|
VertexAI: 'VertexAI',
|
|
269
269
|
xAI: 'xAI',
|
|
270
270
|
Perplexity: 'Perplexity',
|
|
271
|
+
Ollama: 'Ollama',
|
|
271
272
|
} as const;
|
|
272
273
|
// Base provider type
|
|
273
274
|
export type TBuiltinLLMProvider = (typeof BuiltinLLMProviders)[keyof typeof BuiltinLLMProviders];
|