onairos 2.0.7 → 2.0.9

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.
@@ -1,290 +0,0 @@
1
- import { Pinecone } from '@pinecone-database/pinecone';
2
- import OpenAI from 'openai';
3
- import { extractMemory } from '../utils/extractMemory.js';
4
- import { v4 as uuidv4 } from 'uuid';
5
-
6
- /**
7
- * Memory Manager for RAG functionality
8
- * Handles vector storage, memory extraction, and retrieval
9
- */
10
- export class MemoryManager {
11
- constructor({ pineconeApiKey, pineconeEnvironment, indexName, openaiApiKey }) {
12
- this.pinecone = new Pinecone({
13
- apiKey: pineconeApiKey,
14
- environment: pineconeEnvironment
15
- });
16
-
17
- this.openai = new OpenAI({ apiKey: openaiApiKey });
18
- this.indexName = indexName || 'onairos-memory';
19
- this.index = null;
20
- this.embeddingModel = 'text-embedding-3-small';
21
- this.embeddingDimension = 1536;
22
- }
23
-
24
- /**
25
- * Initialize the memory manager and create index if needed
26
- */
27
- async initialize() {
28
- try {
29
- // Check if index exists
30
- const indexList = await this.pinecone.listIndexes();
31
- const indexExists = indexList.indexes?.some(index => index.name === this.indexName);
32
-
33
- if (!indexExists) {
34
- // Create index if it doesn't exist
35
- await this.pinecone.createIndex({
36
- name: this.indexName,
37
- dimension: this.embeddingDimension,
38
- metric: 'cosine',
39
- spec: {
40
- serverless: {
41
- cloud: 'aws',
42
- region: 'us-east-1'
43
- }
44
- }
45
- });
46
-
47
- // Wait for index to be ready
48
- await this._waitForIndexReady();
49
- }
50
-
51
- this.index = this.pinecone.index(this.indexName);
52
- } catch (error) {
53
- console.warn(`Failed to initialize Pinecone index: ${error.message}`);
54
- // Continue without vector storage for development
55
- }
56
- }
57
-
58
- /**
59
- * Wait for index to be ready
60
- */
61
- async _waitForIndexReady() {
62
- let attempts = 0;
63
- const maxAttempts = 30;
64
-
65
- while (attempts < maxAttempts) {
66
- try {
67
- const indexStats = await this.pinecone.describeIndex(this.indexName);
68
- if (indexStats.status?.ready) {
69
- return;
70
- }
71
- } catch (error) {
72
- // Index might not be ready yet
73
- }
74
-
75
- await new Promise(resolve => setTimeout(resolve, 2000));
76
- attempts++;
77
- }
78
-
79
- throw new Error('Index failed to become ready within timeout');
80
- }
81
-
82
- /**
83
- * Generate embedding for text using OpenAI
84
- * @param {string} text - Text to embed
85
- * @returns {Promise<Array>} Embedding vector
86
- */
87
- async getEmbedding(text) {
88
- try {
89
- const response = await this.openai.embeddings.create({
90
- model: this.embeddingModel,
91
- input: text.substring(0, 8000), // Limit text length
92
- encoding_format: 'float'
93
- });
94
-
95
- return response.data[0].embedding;
96
- } catch (error) {
97
- console.error('Failed to generate embedding:', error.message);
98
- return null;
99
- }
100
- }
101
-
102
- /**
103
- * Store interaction in vector database
104
- * @param {string} userId - User identifier
105
- * @param {string} query - User query
106
- * @param {string} response - Assistant response
107
- */
108
- async storeInteraction(userId, query, response) {
109
- try {
110
- // Extract selected memory data (not raw interactions)
111
- const memoryData = extractMemory({ query, response });
112
-
113
- if (!memoryData || !this.index) {
114
- return; // Skip if no meaningful memory or no index
115
- }
116
-
117
- // Generate embedding for the memory data
118
- const embedding = await this.getEmbedding(memoryData);
119
-
120
- if (!embedding) {
121
- return; // Skip if embedding generation failed
122
- }
123
-
124
- // Store in vector database
125
- const vectorId = `${userId}-${uuidv4()}`;
126
-
127
- await this.index.upsert([{
128
- id: vectorId,
129
- values: embedding,
130
- metadata: {
131
- userId,
132
- data: memoryData,
133
- timestamp: new Date().toISOString(),
134
- query: query.substring(0, 500), // Store truncated query for context
135
- type: 'memory'
136
- }
137
- }]);
138
-
139
- console.log(`Stored memory for user ${userId}: ${memoryData.substring(0, 100)}...`);
140
- } catch (error) {
141
- console.error('Failed to store interaction:', error.message);
142
- }
143
- }
144
-
145
- /**
146
- * Retrieve relevant memory for a query using RAG
147
- * @param {string} userId - User identifier
148
- * @param {string} query - Current query
149
- * @returns {Promise<Array>} Array of relevant memory data
150
- */
151
- async retrieveMemory(userId, query) {
152
- try {
153
- if (!this.index || !query.trim()) {
154
- return [];
155
- }
156
-
157
- // Generate embedding for the query
158
- const queryEmbedding = await this.getEmbedding(query);
159
-
160
- if (!queryEmbedding) {
161
- return [];
162
- }
163
-
164
- // Search for similar memories
165
- const searchResults = await this.index.query({
166
- vector: queryEmbedding,
167
- topK: 5,
168
- filter: {
169
- userId: { $eq: userId },
170
- type: { $eq: 'memory' }
171
- },
172
- includeMetadata: true
173
- });
174
-
175
- // Extract and return relevant memory data
176
- const memories = searchResults.matches
177
- .filter(match => match.score > 0.7) // Only high-confidence matches
178
- .map(match => match.metadata.data)
179
- .filter(data => data && data.length > 0);
180
-
181
- return [...new Set(memories)]; // Remove duplicates
182
- } catch (error) {
183
- console.error('Failed to retrieve memory:', error.message);
184
- return [];
185
- }
186
- }
187
-
188
- /**
189
- * Clear all memory for a specific user
190
- * @param {string} userId - User identifier
191
- */
192
- async clearUserMemory(userId) {
193
- try {
194
- if (!this.index) {
195
- return;
196
- }
197
-
198
- // Query all vectors for the user
199
- const queryResults = await this.index.query({
200
- vector: new Array(this.embeddingDimension).fill(0),
201
- topK: 10000,
202
- filter: {
203
- userId: { $eq: userId }
204
- },
205
- includeMetadata: false
206
- });
207
-
208
- // Delete all user vectors
209
- const vectorIds = queryResults.matches.map(match => match.id);
210
-
211
- if (vectorIds.length > 0) {
212
- await this.index.deleteMany(vectorIds);
213
- console.log(`Cleared ${vectorIds.length} memory entries for user ${userId}`);
214
- }
215
- } catch (error) {
216
- console.error('Failed to clear user memory:', error.message);
217
- }
218
- }
219
-
220
- /**
221
- * Get user memory summary
222
- * @param {string} userId - User identifier
223
- * @returns {Promise<Array>} Array of memory entries with metadata
224
- */
225
- async getUserMemory(userId) {
226
- try {
227
- if (!this.index) {
228
- return [];
229
- }
230
-
231
- const queryResults = await this.index.query({
232
- vector: new Array(this.embeddingDimension).fill(0),
233
- topK: 100,
234
- filter: {
235
- userId: { $eq: userId },
236
- type: { $eq: 'memory' }
237
- },
238
- includeMetadata: true
239
- });
240
-
241
- return queryResults.matches.map(match => ({
242
- id: match.id,
243
- data: match.metadata.data,
244
- timestamp: match.metadata.timestamp,
245
- query: match.metadata.query
246
- }));
247
- } catch (error) {
248
- console.error('Failed to get user memory:', error.message);
249
- return [];
250
- }
251
- }
252
-
253
- /**
254
- * Store custom memory entry
255
- * @param {string} userId - User identifier
256
- * @param {string} memoryData - Memory data to store
257
- * @param {string} category - Memory category (optional)
258
- */
259
- async storeCustomMemory(userId, memoryData, category = 'custom') {
260
- try {
261
- if (!this.index || !memoryData.trim()) {
262
- return;
263
- }
264
-
265
- const embedding = await this.getEmbedding(memoryData);
266
-
267
- if (!embedding) {
268
- return;
269
- }
270
-
271
- const vectorId = `${userId}-${category}-${uuidv4()}`;
272
-
273
- await this.index.upsert([{
274
- id: vectorId,
275
- values: embedding,
276
- metadata: {
277
- userId,
278
- data: memoryData,
279
- timestamp: new Date().toISOString(),
280
- category,
281
- type: 'memory'
282
- }
283
- }]);
284
-
285
- console.log(`Stored custom memory for user ${userId}: ${memoryData.substring(0, 100)}...`);
286
- } catch (error) {
287
- console.error('Failed to store custom memory:', error.message);
288
- }
289
- }
290
- }
@@ -1,152 +0,0 @@
1
- import { LLMWrapper } from './LLMWrapper.js';
2
- import { MemoryManager } from './MemoryManager.js';
3
- import { SessionManager } from './SessionManager.js';
4
-
5
- /**
6
- * Main Onairos SDK Client
7
- * Provides a unified completion API with RAG-enhanced personalization
8
- */
9
- export class OnairosClient {
10
- constructor({
11
- openaiApiKey,
12
- anthropicApiKey,
13
- googleApiKey,
14
- pineconeApiKey,
15
- pineconeEnvironment,
16
- jwtSecret,
17
- indexName = 'onairos-memory'
18
- }) {
19
- this.llmWrapper = new LLMWrapper({
20
- openaiApiKey,
21
- anthropicApiKey,
22
- googleApiKey
23
- });
24
-
25
- this.memoryManager = new MemoryManager({
26
- pineconeApiKey,
27
- pineconeEnvironment,
28
- indexName,
29
- openaiApiKey // For embeddings
30
- });
31
-
32
- this.sessionManager = new SessionManager({ jwtSecret });
33
- }
34
-
35
- /**
36
- * Initialize the client (sets up vector store)
37
- */
38
- async initialize() {
39
- await this.memoryManager.initialize();
40
- }
41
-
42
- /**
43
- * Create a completion with RAG enhancement
44
- * @param {Object} params - Completion parameters
45
- * @param {string} params.model - Model name (e.g., 'gpt-4', 'claude-3-sonnet', 'gemini-pro')
46
- * @param {Array} params.messages - Array of message objects
47
- * @param {string} params.userId - User identifier
48
- * @param {string} params.sessionToken - Session token for validation
49
- * @param {Object} params.options - Additional options (temperature, max_tokens, etc.)
50
- * @returns {Promise<Object>} Completion response
51
- */
52
- async completions({ model, messages, userId, sessionToken, options = {} }) {
53
- try {
54
- // Validate session
55
- const { userId: validatedUserId } = await this.sessionManager.validateSession(sessionToken);
56
- if (validatedUserId !== userId) {
57
- throw new Error('Invalid session: User ID mismatch');
58
- }
59
-
60
- // Extract query text from the last message
61
- const lastMessage = messages[messages.length - 1];
62
- const query = typeof lastMessage.content === 'string'
63
- ? lastMessage.content
64
- : lastMessage.content[0]?.text || '';
65
-
66
- // Retrieve relevant memory data using RAG
67
- const memoryContext = await this.memoryManager.retrieveMemory(userId, query);
68
-
69
- // Construct prompt with RAG context if memory exists
70
- let augmentedMessages = [...messages];
71
- if (memoryContext.length > 0) {
72
- const contextMessage = {
73
- role: 'system',
74
- content: `Context from previous interactions: ${memoryContext.join(' | ')}`
75
- };
76
-
77
- // Insert context before the last user message
78
- augmentedMessages = [
79
- ...messages.slice(0, -1),
80
- contextMessage,
81
- lastMessage
82
- ];
83
- }
84
-
85
- // Call LLM with augmented messages
86
- const response = await this.llmWrapper.createCompletion({
87
- model,
88
- messages: augmentedMessages,
89
- options
90
- });
91
-
92
- // Extract and store selected memory data
93
- await this.memoryManager.storeInteraction(userId, query, response.content);
94
-
95
- return {
96
- id: response.id || `onairos-${Date.now()}`,
97
- object: 'chat.completion',
98
- created: Math.floor(Date.now() / 1000),
99
- model: model,
100
- choices: [{
101
- index: 0,
102
- message: {
103
- role: 'assistant',
104
- content: response.content
105
- },
106
- finish_reason: response.finish_reason || 'stop'
107
- }],
108
- usage: response.usage || {
109
- prompt_tokens: 0,
110
- completion_tokens: 0,
111
- total_tokens: 0
112
- }
113
- };
114
-
115
- } catch (error) {
116
- throw new Error(`Onairos completion failed: ${error.message}`);
117
- }
118
- }
119
-
120
- /**
121
- * Create a completion (alias for completions for OpenAI compatibility)
122
- */
123
- async create(params) {
124
- return this.completions(params);
125
- }
126
-
127
- /**
128
- * Generate a session token for a user
129
- * @param {string} userId - User identifier
130
- * @returns {string} JWT session token
131
- */
132
- generateSessionToken(userId) {
133
- return this.sessionManager.generateSessionToken(userId);
134
- }
135
-
136
- /**
137
- * Clear memory for a specific user
138
- * @param {string} userId - User identifier
139
- */
140
- async clearUserMemory(userId) {
141
- await this.memoryManager.clearUserMemory(userId);
142
- }
143
-
144
- /**
145
- * Get user memory summary
146
- * @param {string} userId - User identifier
147
- * @returns {Promise<Array>} Array of memory entries
148
- */
149
- async getUserMemory(userId) {
150
- return this.memoryManager.getUserMemory(userId);
151
- }
152
- }
@@ -1,257 +0,0 @@
1
- import jwt from 'jsonwebtoken';
2
- import { v4 as uuidv4 } from 'uuid';
3
-
4
- /**
5
- * Session Manager for user authentication and isolation
6
- * Handles JWT token generation and validation
7
- */
8
- export class SessionManager {
9
- constructor({ jwtSecret }) {
10
- if (!jwtSecret) {
11
- throw new Error('JWT secret is required for session management');
12
- }
13
- this.jwtSecret = jwtSecret;
14
- this.defaultExpiration = '24h'; // Default session duration
15
- }
16
-
17
- /**
18
- * Generate a session token for a user
19
- * @param {string} userId - User identifier
20
- * @param {Object} options - Token options
21
- * @param {string} options.expiresIn - Token expiration time
22
- * @param {Object} options.additionalClaims - Additional claims to include
23
- * @returns {string} JWT session token
24
- */
25
- generateSessionToken(userId, options = {}) {
26
- try {
27
- const {
28
- expiresIn = this.defaultExpiration,
29
- additionalClaims = {}
30
- } = options;
31
-
32
- const sessionId = `session_${uuidv4()}`;
33
- const issuedAt = Math.floor(Date.now() / 1000);
34
-
35
- const payload = {
36
- userId,
37
- sessionId,
38
- iat: issuedAt,
39
- type: 'onairos_session',
40
- ...additionalClaims
41
- };
42
-
43
- const token = jwt.sign(payload, this.jwtSecret, {
44
- expiresIn,
45
- issuer: 'onairos-sdk',
46
- audience: 'onairos-client'
47
- });
48
-
49
- return token;
50
- } catch (error) {
51
- throw new Error(`Failed to generate session token: ${error.message}`);
52
- }
53
- }
54
-
55
- /**
56
- * Validate a session token
57
- * @param {string} token - JWT token to validate
58
- * @returns {Promise<Object>} Decoded token payload
59
- */
60
- async validateSession(token) {
61
- return new Promise((resolve, reject) => {
62
- if (!token) {
63
- reject(new Error('No token provided'));
64
- return;
65
- }
66
-
67
- jwt.verify(token, this.jwtSecret, {
68
- issuer: 'onairos-sdk',
69
- audience: 'onairos-client'
70
- }, (err, decoded) => {
71
- if (err) {
72
- if (err.name === 'TokenExpiredError') {
73
- reject(new Error('Session token has expired'));
74
- } else if (err.name === 'JsonWebTokenError') {
75
- reject(new Error('Invalid session token'));
76
- } else {
77
- reject(new Error(`Token validation failed: ${err.message}`));
78
- }
79
- return;
80
- }
81
-
82
- // Validate token type
83
- if (decoded.type !== 'onairos_session') {
84
- reject(new Error('Invalid token type'));
85
- return;
86
- }
87
-
88
- resolve(decoded);
89
- });
90
- });
91
- }
92
-
93
- /**
94
- * Refresh a session token
95
- * @param {string} token - Current token to refresh
96
- * @param {Object} options - Refresh options
97
- * @returns {Promise<string>} New session token
98
- */
99
- async refreshSession(token, options = {}) {
100
- try {
101
- const decoded = await this.validateSession(token);
102
-
103
- // Generate new token with same user ID but new session ID
104
- return this.generateSessionToken(decoded.userId, {
105
- expiresIn: options.expiresIn || this.defaultExpiration,
106
- additionalClaims: options.additionalClaims || {}
107
- });
108
- } catch (error) {
109
- throw new Error(`Failed to refresh session: ${error.message}`);
110
- }
111
- }
112
-
113
- /**
114
- * Extract user ID from token without full validation
115
- * @param {string} token - JWT token
116
- * @returns {string|null} User ID or null if invalid
117
- */
118
- extractUserId(token) {
119
- try {
120
- const decoded = jwt.decode(token);
121
- return decoded?.userId || null;
122
- } catch (error) {
123
- return null;
124
- }
125
- }
126
-
127
- /**
128
- * Check if token is expired
129
- * @param {string} token - JWT token
130
- * @returns {boolean} True if expired
131
- */
132
- isTokenExpired(token) {
133
- try {
134
- const decoded = jwt.decode(token);
135
- if (!decoded || !decoded.exp) {
136
- return true;
137
- }
138
-
139
- const currentTime = Math.floor(Date.now() / 1000);
140
- return decoded.exp < currentTime;
141
- } catch (error) {
142
- return true;
143
- }
144
- }
145
-
146
- /**
147
- * Get token expiration time
148
- * @param {string} token - JWT token
149
- * @returns {Date|null} Expiration date or null if invalid
150
- */
151
- getTokenExpiration(token) {
152
- try {
153
- const decoded = jwt.decode(token);
154
- if (!decoded || !decoded.exp) {
155
- return null;
156
- }
157
-
158
- return new Date(decoded.exp * 1000);
159
- } catch (error) {
160
- return null;
161
- }
162
- }
163
-
164
- /**
165
- * Create a guest session token (for anonymous users)
166
- * @param {Object} options - Token options
167
- * @returns {string} Guest session token
168
- */
169
- generateGuestToken(options = {}) {
170
- const guestId = `guest_${uuidv4()}`;
171
- return this.generateSessionToken(guestId, {
172
- expiresIn: options.expiresIn || '1h',
173
- additionalClaims: {
174
- type: 'guest',
175
- ...options.additionalClaims
176
- }
177
- });
178
- }
179
-
180
- /**
181
- * Validate session and ensure user access
182
- * @param {string} token - Session token
183
- * @param {string} expectedUserId - Expected user ID
184
- * @returns {Promise<Object>} Validation result
185
- */
186
- async validateUserAccess(token, expectedUserId) {
187
- try {
188
- const decoded = await this.validateSession(token);
189
-
190
- if (decoded.userId !== expectedUserId) {
191
- throw new Error('User ID mismatch - access denied');
192
- }
193
-
194
- return {
195
- valid: true,
196
- userId: decoded.userId,
197
- sessionId: decoded.sessionId,
198
- isGuest: decoded.type === 'guest'
199
- };
200
- } catch (error) {
201
- return {
202
- valid: false,
203
- error: error.message
204
- };
205
- }
206
- }
207
-
208
- /**
209
- * Generate API key for server-to-server communication
210
- * @param {string} clientId - Client identifier
211
- * @param {Array} scopes - API scopes
212
- * @returns {string} API key token
213
- */
214
- generateApiKey(clientId, scopes = []) {
215
- try {
216
- const payload = {
217
- clientId,
218
- scopes,
219
- type: 'api_key',
220
- iat: Math.floor(Date.now() / 1000)
221
- };
222
-
223
- return jwt.sign(payload, this.jwtSecret, {
224
- issuer: 'onairos-sdk',
225
- audience: 'onairos-api'
226
- });
227
- } catch (error) {
228
- throw new Error(`Failed to generate API key: ${error.message}`);
229
- }
230
- }
231
-
232
- /**
233
- * Validate API key
234
- * @param {string} apiKey - API key to validate
235
- * @returns {Promise<Object>} Validation result
236
- */
237
- async validateApiKey(apiKey) {
238
- return new Promise((resolve, reject) => {
239
- jwt.verify(apiKey, this.jwtSecret, {
240
- issuer: 'onairos-sdk',
241
- audience: 'onairos-api'
242
- }, (err, decoded) => {
243
- if (err) {
244
- reject(new Error(`Invalid API key: ${err.message}`));
245
- return;
246
- }
247
-
248
- if (decoded.type !== 'api_key') {
249
- reject(new Error('Invalid key type'));
250
- return;
251
- }
252
-
253
- resolve(decoded);
254
- });
255
- });
256
- }
257
- }