@ixo/common 1.1.31 → 1.1.33
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/.turbo/turbo-build.log +1 -1
- package/dist/ai/models/openai.d.ts +16 -0
- package/dist/ai/models/openai.d.ts.map +1 -1
- package/dist/ai/models/openai.js +26 -0
- package/dist/ai/models/openai.js.map +1 -1
- package/dist/ai/nodes/generic-chat/generic-chat.node.d.ts +1 -1
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts +1 -2
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts.map +1 -1
- package/dist/ai/semantic-router-factory/create-semantic-router.js +2 -3
- package/dist/ai/semantic-router-factory/create-semantic-router.js.map +1 -1
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts +1 -1
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts.map +1 -1
- package/dist/ai/tools/parser-action-tool.d.ts +1 -1
- package/dist/ai/tools/parser-action-tool.d.ts.map +1 -1
- package/dist/ai/tools/parser-browser-tool.d.ts +1 -1
- package/dist/ai/tools/parser-browser-tool.d.ts.map +1 -1
- package/dist/ai/tools/scrape-web-page.d.ts +1 -1
- package/dist/ai/tools/scrape-web-page.d.ts.map +1 -1
- package/dist/ai/tools/web-search-tool.d.ts +1 -1
- package/dist/ai/tools/web-search-tool.d.ts.map +1 -1
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts +10 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts.map +1 -1
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js +2 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js.map +1 -1
- package/dist/services/env/env.service.js.map +1 -1
- package/dist/services/session-manager/session-manager.service.d.ts +2 -2
- package/dist/services/session-manager/session-manager.service.d.ts.map +1 -1
- package/dist/services/session-manager/session-manager.service.js +48 -26
- package/dist/services/session-manager/session-manager.service.js.map +1 -1
- package/package.json +13 -14
- package/src/ai/models/openai.ts +31 -0
- package/src/ai/semantic-router-factory/create-semantic-router.test.ts +0 -3
- package/src/ai/semantic-router-factory/create-semantic-router.ts +2 -7
- package/src/ai/utils/transformGraphStateMessageToListMessageResponse.ts +16 -0
- package/src/services/env/env.service.ts +1 -1
- package/src/services/session-manager/session-manager.service.ts +57 -25
- package/tsconfig.tsbuildinfo +1 -1
package/src/ai/models/openai.ts
CHANGED
|
@@ -8,6 +8,37 @@ import {
|
|
|
8
8
|
} from '@langchain/openai';
|
|
9
9
|
import OpenAI, { type ClientOptions } from 'openai';
|
|
10
10
|
|
|
11
|
+
export type LLMProvider = 'openrouter' | 'nebius';
|
|
12
|
+
|
|
13
|
+
export function getLLMProvider(): LLMProvider {
|
|
14
|
+
const raw = (process.env.LLM_PROVIDER ?? 'openrouter').toLowerCase();
|
|
15
|
+
if (raw === 'nebius') return 'nebius';
|
|
16
|
+
return 'openrouter';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Provider-aware base URL and API key. */
|
|
20
|
+
export function getProviderConfig() {
|
|
21
|
+
const provider = getLLMProvider();
|
|
22
|
+
|
|
23
|
+
if (provider === 'nebius') {
|
|
24
|
+
return {
|
|
25
|
+
provider,
|
|
26
|
+
baseURL: 'https://api.tokenfactory.nebius.com/v1/',
|
|
27
|
+
apiKey: process.env.NEBIUS_API_KEY ?? '',
|
|
28
|
+
headers: {} as Record<string, string>,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
provider,
|
|
34
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
35
|
+
apiKey: process.env.OPEN_ROUTER_API_KEY ?? '',
|
|
36
|
+
headers: {
|
|
37
|
+
'HTTP-Referer': 'oracle-app.com',
|
|
38
|
+
'X-Title': process.env.ORACLE_NAME ?? 'Oracle App',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
11
42
|
const getChatOpenAiModel = (params?: ChatOpenAIFields): ChatOpenAI =>
|
|
12
43
|
new ChatOpenAI({
|
|
13
44
|
temperature: 0.2,
|
|
@@ -2,9 +2,6 @@ import { createSemanticRouter } from './create-semantic-router.js';
|
|
|
2
2
|
|
|
3
3
|
const parse = vi.fn();
|
|
4
4
|
const create = vi.fn();
|
|
5
|
-
vi.mock('langfuse', () => ({
|
|
6
|
-
observeOpenAI: vi.fn((client: unknown) => client),
|
|
7
|
-
}));
|
|
8
5
|
vi.mock('openai', () => ({
|
|
9
6
|
OpenAI: vi.fn().mockImplementation(() => {
|
|
10
7
|
function fn(): unknown {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Logger } from '@ixo/logger';
|
|
2
2
|
import { PromptTemplate } from '@langchain/core/prompts';
|
|
3
|
-
import { type LangfuseConfig, observeOpenAI } from 'langfuse';
|
|
4
3
|
import { OpenAI } from 'openai';
|
|
5
4
|
import { zodResponseFormat } from 'openai/helpers/zod';
|
|
6
5
|
import z from 'zod';
|
|
@@ -39,10 +38,7 @@ export const createSemanticRouter = <
|
|
|
39
38
|
| 'gpt-4.1-nano'
|
|
40
39
|
| 'gpt-4.1-mini' = 'gpt-4.1-mini',
|
|
41
40
|
isComplex = false,
|
|
42
|
-
): ((
|
|
43
|
-
state: EnsureKeys<Record<string, unknown>, K>,
|
|
44
|
-
traceConfig?: LangfuseConfig,
|
|
45
|
-
) => Promise<keyof R>) => {
|
|
41
|
+
): ((state: EnsureKeys<Record<string, unknown>, K>) => Promise<keyof R>) => {
|
|
46
42
|
const keys = validateRoutes(routes, basedOn);
|
|
47
43
|
const schema = z.object({
|
|
48
44
|
nextRoute: z.enum(
|
|
@@ -52,7 +48,6 @@ export const createSemanticRouter = <
|
|
|
52
48
|
});
|
|
53
49
|
return async <T extends Record<string, unknown>>(
|
|
54
50
|
state: EnsureKeys<T, K>,
|
|
55
|
-
traceConfig?: LangfuseConfig,
|
|
56
51
|
): Promise<keyof R> => {
|
|
57
52
|
const selectedValues = {} as Record<string, string | object>;
|
|
58
53
|
for (const key of basedOn) {
|
|
@@ -72,7 +67,7 @@ export const createSemanticRouter = <
|
|
|
72
67
|
// find the route that matches the state
|
|
73
68
|
const prompt = PromptTemplate.fromTemplate(semanticRouterPrompt);
|
|
74
69
|
|
|
75
|
-
const client =
|
|
70
|
+
const client = new OpenAI();
|
|
76
71
|
const promptWithState = await prompt.format({
|
|
77
72
|
routes: jsonToYaml(routes),
|
|
78
73
|
state: jsonToYaml(selectedValues),
|
|
@@ -14,6 +14,15 @@ interface ToolCall {
|
|
|
14
14
|
output?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface AttachmentMeta {
|
|
18
|
+
filename: string;
|
|
19
|
+
mimetype: string;
|
|
20
|
+
size?: number;
|
|
21
|
+
mxcUri?: string;
|
|
22
|
+
eventId?: string;
|
|
23
|
+
category: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
interface MessageDto {
|
|
18
27
|
id: string;
|
|
19
28
|
type: 'ai' | 'human';
|
|
@@ -22,6 +31,7 @@ interface MessageDto {
|
|
|
22
31
|
reasoning?: string;
|
|
23
32
|
isComplete?: boolean;
|
|
24
33
|
isReasoning?: boolean;
|
|
34
|
+
attachment?: AttachmentMeta;
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
export interface ListOracleMessagesResponse {
|
|
@@ -36,6 +46,7 @@ export interface CleanAdditionalKwargs {
|
|
|
36
46
|
type: string;
|
|
37
47
|
text: string;
|
|
38
48
|
}>;
|
|
49
|
+
attachment?: AttachmentMeta;
|
|
39
50
|
[key: string]: unknown; // Allow additional properties for LangChain compatibility
|
|
40
51
|
}
|
|
41
52
|
|
|
@@ -55,6 +66,10 @@ export function transformGraphStateMessageToListMessageResponse(
|
|
|
55
66
|
message.additional_kwargs as CleanAdditionalKwargs;
|
|
56
67
|
const reasoning = additionalKwargs?.reasoning;
|
|
57
68
|
|
|
69
|
+
// Extract attachment metadata for human messages
|
|
70
|
+
const attachment =
|
|
71
|
+
message.type === 'human' ? additionalKwargs?.attachment : undefined;
|
|
72
|
+
|
|
58
73
|
acc.push({
|
|
59
74
|
type: message.type === 'ai' ? 'ai' : 'human',
|
|
60
75
|
content: String(message.content),
|
|
@@ -68,6 +83,7 @@ export function transformGraphStateMessageToListMessageResponse(
|
|
|
68
83
|
reasoning,
|
|
69
84
|
isComplete: true, // Messages from DB are always complete
|
|
70
85
|
isReasoning: false, // since this is not a reasoning message and the request is done
|
|
86
|
+
...(attachment && { attachment }),
|
|
71
87
|
});
|
|
72
88
|
}
|
|
73
89
|
if (toolMsg) {
|
|
@@ -11,7 +11,7 @@ export class EnvService<T extends z.ZodType> {
|
|
|
11
11
|
private constructor(schema: T, onError?: (error: z.ZodError) => void) {
|
|
12
12
|
try {
|
|
13
13
|
// Parse and validate environment variables
|
|
14
|
-
this.validatedEnv = schema.parse(process.env) as z.infer<T>;
|
|
14
|
+
this.validatedEnv = schema.parse(process.env) as unknown as z.infer<T>;
|
|
15
15
|
} catch (error) {
|
|
16
16
|
if (error instanceof z.ZodError) {
|
|
17
17
|
if (onError) {
|
|
@@ -2,7 +2,12 @@ import { Logger } from '@ixo/logger';
|
|
|
2
2
|
import { MatrixManager } from '@ixo/matrix';
|
|
3
3
|
import { getMatrixHomeServerCroppedForDid } from '@ixo/oracles-chain-client';
|
|
4
4
|
import { type Database } from 'better-sqlite3';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getChatOpenAiModel,
|
|
7
|
+
getLLMProvider,
|
|
8
|
+
getOpenRouterChatModel,
|
|
9
|
+
getProviderConfig,
|
|
10
|
+
} from '../../ai/index.js';
|
|
6
11
|
import { type MemoryEngineService } from '../memory-engine/memory-engine.service.js';
|
|
7
12
|
import { type UserContextData } from '../memory-engine/types.js';
|
|
8
13
|
import {
|
|
@@ -41,18 +46,28 @@ export class SessionManagerService {
|
|
|
41
46
|
if (messages.length === 0) {
|
|
42
47
|
return 'Untitled';
|
|
43
48
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const provider = getLLMProvider();
|
|
50
|
+
const config = getProviderConfig();
|
|
51
|
+
|
|
52
|
+
const llm =
|
|
53
|
+
provider === 'openrouter'
|
|
54
|
+
? getOpenRouterChatModel({
|
|
55
|
+
model: 'meta-llama/llama-3.1-8b-instruct',
|
|
56
|
+
temperature: 0.3,
|
|
57
|
+
timeout: 60_000,
|
|
58
|
+
})
|
|
59
|
+
: getChatOpenAiModel({
|
|
60
|
+
model: 'meta-llama/Meta-Llama-3.1-8B-Instruct',
|
|
61
|
+
temperature: 0.3,
|
|
62
|
+
apiKey: config.apiKey,
|
|
63
|
+
timeout: 60_000,
|
|
64
|
+
configuration: {
|
|
65
|
+
baseURL: config.baseURL,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
53
68
|
const response = await llm.invoke(
|
|
54
|
-
`Based on this messages messages, Add a title for this convo and only based on the messages? MAKE SURE TO ONLY RESPOND WITH THE TITLE.
|
|
55
|
-
|
|
69
|
+
`Based on this messages messages, Add a title for this convo and only based on the messages? MAKE SURE TO ONLY RESPOND WITH THE TITLE.
|
|
70
|
+
|
|
56
71
|
## RESPONSE FORMAT
|
|
57
72
|
ONLY RESPOND WITH THE TITLE not anything else that title will be saved to the store directly from your response so generated based on the messages.
|
|
58
73
|
|
|
@@ -125,7 +140,7 @@ ___________________________________________________________
|
|
|
125
140
|
messages,
|
|
126
141
|
oracleEntityDid,
|
|
127
142
|
oracleName,
|
|
128
|
-
roomId
|
|
143
|
+
roomId,
|
|
129
144
|
lastProcessedCount,
|
|
130
145
|
oracleDid,
|
|
131
146
|
userContext,
|
|
@@ -158,6 +173,7 @@ ___________________________________________________________
|
|
|
158
173
|
oracleEntityDid,
|
|
159
174
|
oracleDid,
|
|
160
175
|
userContext,
|
|
176
|
+
roomId,
|
|
161
177
|
slackThreadTs,
|
|
162
178
|
};
|
|
163
179
|
|
|
@@ -165,8 +181,8 @@ ___________________________________________________________
|
|
|
165
181
|
db.prepare(
|
|
166
182
|
`
|
|
167
183
|
INSERT INTO sessions (
|
|
168
|
-
session_id, title, last_updated_at, created_at, oracle_name,
|
|
169
|
-
oracle_did, oracle_entity_did, last_processed_count,
|
|
184
|
+
session_id, title, last_updated_at, created_at, oracle_name,
|
|
185
|
+
oracle_did, oracle_entity_did, last_processed_count,
|
|
170
186
|
user_context, room_id, slack_thread_ts
|
|
171
187
|
)
|
|
172
188
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -203,6 +219,19 @@ ___________________________________________________________
|
|
|
203
219
|
})
|
|
204
220
|
: selectedSession.title;
|
|
205
221
|
|
|
222
|
+
if (allowTitleUpdate && roomId && title) {
|
|
223
|
+
this.matrixManger
|
|
224
|
+
.editMessage({
|
|
225
|
+
messageId: sessionId,
|
|
226
|
+
roomId,
|
|
227
|
+
message: title,
|
|
228
|
+
isOracleAdmin: true,
|
|
229
|
+
})
|
|
230
|
+
.catch((err) => {
|
|
231
|
+
Logger.error('Failed to update conversation title in Matrix:', err);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
206
235
|
const lastUpdatedAt = new Date().toISOString();
|
|
207
236
|
const updatedSession: ChatSession = {
|
|
208
237
|
...selectedSession,
|
|
@@ -214,7 +243,7 @@ ___________________________________________________________
|
|
|
214
243
|
|
|
215
244
|
db.prepare(
|
|
216
245
|
`
|
|
217
|
-
UPDATE sessions
|
|
246
|
+
UPDATE sessions
|
|
218
247
|
SET title = ?, last_updated_at = ?, last_processed_count = ?, slack_thread_ts = ?
|
|
219
248
|
WHERE session_id = ?
|
|
220
249
|
`,
|
|
@@ -237,11 +266,11 @@ ___________________________________________________________
|
|
|
237
266
|
const db = await this.syncService.getUserDatabase(did);
|
|
238
267
|
const row = db
|
|
239
268
|
.prepare(
|
|
240
|
-
`SELECT
|
|
269
|
+
`SELECT
|
|
241
270
|
session_id, title, last_updated_at, created_at, oracle_name,
|
|
242
271
|
oracle_did, oracle_entity_did, last_processed_count,
|
|
243
272
|
user_context, room_id, slack_thread_ts
|
|
244
|
-
FROM sessions
|
|
273
|
+
FROM sessions
|
|
245
274
|
WHERE session_id = ?`,
|
|
246
275
|
)
|
|
247
276
|
.get(sessionId) as
|
|
@@ -300,12 +329,12 @@ ___________________________________________________________
|
|
|
300
329
|
// Get paginated sessions with total count
|
|
301
330
|
const rows = db
|
|
302
331
|
.prepare(
|
|
303
|
-
`SELECT
|
|
332
|
+
`SELECT
|
|
304
333
|
session_id, title, last_updated_at, created_at, oracle_name,
|
|
305
334
|
oracle_did, oracle_entity_did, last_processed_count,
|
|
306
335
|
user_context, room_id, slack_thread_ts,
|
|
307
336
|
COUNT(*) OVER() as total
|
|
308
|
-
FROM sessions
|
|
337
|
+
FROM sessions
|
|
309
338
|
ORDER BY last_updated_at DESC
|
|
310
339
|
LIMIT ? OFFSET ?`,
|
|
311
340
|
)
|
|
@@ -346,6 +375,7 @@ ___________________________________________________________
|
|
|
346
375
|
|
|
347
376
|
public async createSession(
|
|
348
377
|
createSessionDto: CreateChatSessionDto,
|
|
378
|
+
overrideEventId?: string,
|
|
349
379
|
): Promise<CreateChatSessionResponseDto> {
|
|
350
380
|
const userHomeServer =
|
|
351
381
|
createSessionDto.homeServer ||
|
|
@@ -359,11 +389,13 @@ ___________________________________________________________
|
|
|
359
389
|
if (!roomId) {
|
|
360
390
|
throw new Error('Room ID not found');
|
|
361
391
|
}
|
|
362
|
-
const eventId =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
392
|
+
const eventId =
|
|
393
|
+
overrideEventId ??
|
|
394
|
+
(await this.matrixManger.sendMessage({
|
|
395
|
+
message: 'New Conversation Started',
|
|
396
|
+
roomId,
|
|
397
|
+
isOracleAdmin: true,
|
|
398
|
+
}));
|
|
367
399
|
|
|
368
400
|
// Gather user context from Memory Engine
|
|
369
401
|
let userContext: UserContextData | undefined;
|