oblien 1.1.2 → 1.1.4

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/README.md CHANGED
@@ -87,7 +87,7 @@ const overview = await agentInstance.getOverview({ days: 7 });
87
87
 
88
88
  ### 💬 Chat Module
89
89
 
90
- Create and manage chat sessions with guest support.
90
+ Create sessions, send messages, and manage guests with streaming support.
91
91
 
92
92
  ```javascript
93
93
  import { OblienChat } from 'oblien/chat';
@@ -100,6 +100,27 @@ const session = await chat.createSession({
100
100
  namespace: 'production'
101
101
  });
102
102
 
103
+ // Send message with streaming
104
+ await chat.send({
105
+ token: session.token,
106
+ message: 'Tell me about AI',
107
+ stream: true,
108
+ onChunk: (data) => console.log(data)
109
+ });
110
+
111
+ // Upload files
112
+ const uploadResult = await chat.upload({
113
+ token: session.token,
114
+ files: fileArray
115
+ });
116
+
117
+ // Send message with uploaded files
118
+ await chat.send({
119
+ token: session.token,
120
+ message: 'Analyze these files',
121
+ uploadId: uploadResult.uploadId
122
+ });
123
+
103
124
  // Create guest session
104
125
  const guestSession = await chat.createGuestSession({
105
126
  ip: '192.168.1.1',
@@ -107,17 +128,20 @@ const guestSession = await chat.createGuestSession({
107
128
  agentId: 'agent-id'
108
129
  });
109
130
 
110
- // List sessions
111
- const sessions = await chat.listSessions({ limit: 20 });
131
+ // Get guest usage
132
+ const usage = await chat.getGuestUsage(guestSession.token);
112
133
  ```
113
134
 
114
135
  **Features:**
115
- - ✅ Session management
116
- - ✅ Guest sessions with fingerprint tracking
117
- - ✅ Token generation
118
- - ✅ Automatic guest ID generation
136
+ - ✅ Session management (create, list, delete)
137
+ - ✅ Message sending with streaming support
138
+ - ✅ File uploads for agent analysis
139
+ - ✅ Guest sessions with IP + fingerprint tracking
140
+ - ✅ Guest usage monitoring and rate limiting
141
+ - ✅ Hybrid mode (works with token or client credentials)
142
+ - ✅ Cache statistics for monitoring
119
143
 
120
- 📖 [Documentation](./docs/CHAT.md) | 💡 [Examples](./examples/chat-example.js)
144
+ 📖 [Full Documentation](./docs/CHAT.md) | 💡 [Examples](./examples/chat-example.js)
121
145
 
122
146
  ---
123
147
 
@@ -517,6 +541,14 @@ MIT License - see LICENSE file for details
517
541
 
518
542
  ## Changelog
519
543
 
544
+ ### v1.3.0 (Latest)
545
+ - ✅ Added `send()` method to Chat module with streaming support
546
+ - ✅ Added `upload()` method for file attachments
547
+ - ✅ Added guest usage monitoring (`getGuestUsage()`)
548
+ - ✅ Added cache statistics (`getCacheStatistics()`)
549
+ - ✅ Hybrid mode support (token or client credentials)
550
+ - ✅ Complete Chat documentation with examples
551
+
520
552
  ### v1.2.0
521
553
  - ✅ Added Sandboxes module
522
554
  - ✅ Enhanced Agents module with proper settings sections
package/index.d.ts CHANGED
@@ -164,17 +164,60 @@ export interface GuestSessionData extends SessionData {
164
164
  };
165
165
  }
166
166
 
167
+ export interface SendMessageOptions {
168
+ token?: string;
169
+ message: string;
170
+ uploadId?: string;
171
+ files?: any[];
172
+ stream?: boolean;
173
+ onChunk?: (data: any) => void;
174
+ onError?: (error: Error) => void;
175
+ onComplete?: () => void;
176
+ metadata?: Record<string, any>;
177
+ }
178
+
179
+ export interface UploadOptions {
180
+ token?: string;
181
+ files: any[] | any;
182
+ metadata?: Record<string, any>;
183
+ }
184
+
185
+ export interface GuestUsageInfo {
186
+ success: boolean;
187
+ namespace: string;
188
+ requestCount: number;
189
+ limit: number;
190
+ remaining: number;
191
+ resetAt?: Date;
192
+ }
193
+
194
+ export interface CacheStatistics {
195
+ success: boolean;
196
+ cache: {
197
+ keys: number;
198
+ hits: number;
199
+ misses: number;
200
+ hitRate: number;
201
+ keysSize: number;
202
+ valuesSize: number;
203
+ };
204
+ }
205
+
167
206
  export class OblienChat {
168
207
  constructor(client: OblienClient, options?: ChatOptions);
169
208
 
170
209
  createSession(options: CreateSessionOptions): Promise<SessionData>;
171
210
  createGuestSession(options: CreateGuestSessionOptions): Promise<GuestSessionData>;
172
211
  getGuest(ip: string, fingerprint?: string): Promise<Guest | null>;
212
+ send(options: SendMessageOptions): Promise<any>;
213
+ upload(options: UploadOptions): Promise<any>;
173
214
  getSession(sessionId: string): Promise<any>;
174
215
  listSessions(options?: Record<string, any>): Promise<any[]>;
175
216
  deleteSession(sessionId: string): Promise<any>;
176
217
  getAllGuests(): Promise<Guest[]>;
177
218
  cleanupGuests(): Promise<number>;
219
+ getGuestUsage(token: string): Promise<GuestUsageInfo>;
220
+ getCacheStatistics(): Promise<CacheStatistics>;
178
221
  }
179
222
 
180
223
  // ============ Namespaces ============
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oblien",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Server-side SDK for Oblien AI Platform - Build AI-powered applications with chat, agents, and workflows",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/chat/index.js CHANGED
@@ -45,10 +45,9 @@ export class OblienChat {
45
45
  async createSession(options) {
46
46
  const session = new ChatSession({
47
47
  client: this.client,
48
- ...options,
49
48
  });
50
49
 
51
- return await session.create();
50
+ return await session.create(options);
52
51
  }
53
52
 
54
53
  /**
@@ -80,6 +79,9 @@ export class OblienChat {
80
79
  // Create session
81
80
  const session = new ChatSession({
82
81
  client: this.client,
82
+ });
83
+
84
+ const sessionData = await session.create({
83
85
  agentId,
84
86
  workflowId,
85
87
  workspace,
@@ -91,8 +93,6 @@ export class OblienChat {
91
93
  endUserId: endUserId,
92
94
  });
93
95
 
94
- const sessionData = await session.create();
95
-
96
96
  // Link session to guest
97
97
  await this.guestManager.addSession(guest.id, sessionData.sessionId);
98
98
 
@@ -138,6 +138,205 @@ export class OblienChat {
138
138
  return null;
139
139
  }
140
140
 
141
+ /**
142
+ * Send a message in a chat session
143
+ * Hybrid mode: Works with session token OR client credentials
144
+ *
145
+ * @param {Object} options - Send options
146
+ * @param {string} [options.token] - Session token (optional, uses client credentials if not provided)
147
+ * @param {string} options.message - Message to send
148
+ * @param {string} [options.uploadId] - Upload ID for attached files
149
+ * @param {Array} [options.files] - File attachments (alternative to uploadId)
150
+ * @param {boolean} [options.stream] - Enable streaming response
151
+ * @param {Function} [options.onChunk] - Callback for streaming chunks (data) => void
152
+ * @param {Function} [options.onError] - Callback for errors (error) => void
153
+ * @param {Function} [options.onComplete] - Callback when stream completes () => void
154
+ * @param {Object} [options.metadata] - Additional metadata
155
+ * @returns {Promise<Object>} Response data
156
+ */
157
+ async send(options = {}) {
158
+ const {
159
+ token,
160
+ message,
161
+ uploadId,
162
+ files,
163
+ stream = false,
164
+ onChunk,
165
+ onError,
166
+ onComplete,
167
+ metadata = {}
168
+ } = options;
169
+
170
+ if (!message || !message.trim()) {
171
+ throw new Error('Message is required');
172
+ }
173
+
174
+ const payload = {
175
+ message,
176
+ stream,
177
+ ...metadata,
178
+ };
179
+
180
+ if (uploadId) {
181
+ payload.upload_id = uploadId;
182
+ }
183
+
184
+ if (files) {
185
+ payload.files = files;
186
+ }
187
+
188
+ // Build headers - use token if provided, otherwise use client credentials
189
+ const headers = {
190
+ ...this.client.getAuthHeaders(),
191
+ 'Content-Type': 'application/json',
192
+ };
193
+
194
+ if (token) {
195
+ headers['Authorization'] = `Bearer ${token}`;
196
+ }
197
+
198
+ const url = this.client._buildURL('ai/chat/send');
199
+
200
+ if (stream) {
201
+ // Handle streaming response
202
+ const response = await fetch(url, {
203
+ method: 'POST',
204
+ headers,
205
+ body: JSON.stringify(payload),
206
+ });
207
+
208
+ if (!response.ok) {
209
+ const error = await response.text();
210
+ throw new Error(error || `API error: ${response.status}`);
211
+ }
212
+
213
+ // Process SSE stream
214
+ const reader = response.body.getReader();
215
+ const decoder = new TextDecoder();
216
+ let buffer = '';
217
+
218
+ try {
219
+ while (true) {
220
+ const { done, value } = await reader.read();
221
+
222
+ if (done) {
223
+ if (onComplete) onComplete();
224
+ break;
225
+ }
226
+
227
+ buffer += decoder.decode(value, { stream: true });
228
+ const lines = buffer.split('\n');
229
+ buffer = lines.pop() || '';
230
+
231
+ for (const line of lines) {
232
+ if (line.trim() === '') continue;
233
+
234
+ try {
235
+ const data = JSON.parse(line);
236
+ if (onChunk) onChunk(data);
237
+ } catch (e) {
238
+ // Skip non-JSON lines (SSE format)
239
+ if (line.startsWith('data: ')) {
240
+ try {
241
+ const jsonStr = line.substring(6);
242
+ const data = JSON.parse(jsonStr);
243
+ if (onChunk) onChunk(data);
244
+ } catch (parseError) {
245
+ console.warn('Failed to parse chunk:', line);
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ return { success: true, stream: true };
253
+ } catch (error) {
254
+ if (onError) onError(error);
255
+ throw error;
256
+ }
257
+ } else {
258
+ // Regular non-streaming request
259
+ const response = await fetch(url, {
260
+ method: 'POST',
261
+ headers,
262
+ body: JSON.stringify(payload),
263
+ });
264
+
265
+ if (!response.ok) {
266
+ const error = await response.json().catch(() => ({ message: response.statusText }));
267
+ throw new Error(error.message || error.error || `API error: ${response.status}`);
268
+ }
269
+
270
+ return await response.json();
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Upload files for a chat session
276
+ * Hybrid mode: Works with session token OR client credentials
277
+ *
278
+ * @param {Object} options - Upload options
279
+ * @param {string} [options.token] - Session token (optional, uses client credentials if not provided)
280
+ * @param {Array|Object} options.files - Files to upload
281
+ * @param {Object} [options.metadata] - Additional metadata
282
+ * @returns {Promise<Object>} Upload result with uploadId
283
+ */
284
+ async upload(options = {}) {
285
+ const { token, files, metadata } = options;
286
+
287
+ if (!files || (Array.isArray(files) && files.length === 0)) {
288
+ throw new Error('Files are required');
289
+ }
290
+
291
+ const formData = new FormData();
292
+
293
+ // Handle both single file and array of files
294
+ const fileArray = Array.isArray(files) ? files : [files];
295
+
296
+ fileArray.forEach((file, index) => {
297
+ if (file instanceof File || file instanceof Blob) {
298
+ formData.append('files', file);
299
+ } else if (file.path && file.buffer) {
300
+ // Node.js file object
301
+ formData.append('files', file.buffer, file.originalname || file.path);
302
+ } else {
303
+ throw new Error(`Invalid file at index ${index}`);
304
+ }
305
+ });
306
+
307
+ // Add any additional options
308
+ if (metadata) {
309
+ formData.append('metadata', JSON.stringify(metadata));
310
+ }
311
+
312
+ // Build headers - use token if provided, otherwise use client credentials
313
+ const headers = {
314
+ ...this.client.getAuthHeaders(),
315
+ };
316
+
317
+ if (token) {
318
+ headers['Authorization'] = `Bearer ${token}`;
319
+ }
320
+
321
+ // Remove Content-Type for FormData (browser will set it with boundary)
322
+ delete headers['Content-Type'];
323
+
324
+ const url = this.client._buildURL('ai/chat/upload');
325
+
326
+ const response = await fetch(url, {
327
+ method: 'POST',
328
+ headers,
329
+ body: formData,
330
+ });
331
+
332
+ if (!response.ok) {
333
+ const error = await response.json().catch(() => ({ message: response.statusText }));
334
+ throw new Error(error.message || error.error || `API error: ${response.status}`);
335
+ }
336
+
337
+ return await response.json();
338
+ }
339
+
141
340
  /**
142
341
  * Get session info
143
342
  * @param {string} sessionId - Session ID
@@ -150,11 +349,23 @@ export class OblienChat {
150
349
  /**
151
350
  * List sessions
152
351
  * @param {Object} [options] - Query options
352
+ * @param {string} [options.namespace] - Filter by namespace
353
+ * @param {string} [options.agentId] - Filter by agent ID
354
+ * @param {string} [options.endUserId] - Filter by end user ID
355
+ * @param {number} [options.limit] - Number of results (max 100)
356
+ * @param {number} [options.offset] - Offset for pagination
357
+ * @param {string} [options.search] - Search in title and user ID
358
+ * @param {string} [options.sortBy] - Sort by 'time' or 'tokens'
359
+ * @param {string} [options.sortOrder] - 'asc' or 'desc'
360
+ * @param {boolean} [options.includeStats] - Include message count and tokens
153
361
  * @returns {Promise<Array>} Array of sessions
154
362
  */
155
363
  async listSessions(options = {}) {
156
- const data = await this.client.get('ai/session', options);
157
- return data.sessions || data;
364
+ const session = new ChatSession({
365
+ client: this.client,
366
+ });
367
+
368
+ return await session.list(options);
158
369
  }
159
370
 
160
371
  /**
@@ -181,6 +392,43 @@ export class OblienChat {
181
392
  async cleanupGuests() {
182
393
  return await this.guestManager.cleanup();
183
394
  }
395
+
396
+ /**
397
+ * Get guest usage information (requires session token)
398
+ * @param {string} token - Session token from guest session
399
+ * @returns {Promise<Object>} Usage information for the guest
400
+ */
401
+ async getGuestUsage(token) {
402
+ if (!token) {
403
+ throw new Error('Session token is required');
404
+ }
405
+
406
+ const headers = {
407
+ ...this.client.getAuthHeaders(),
408
+ 'Authorization': `Bearer ${token}`,
409
+ };
410
+
411
+ const response = await fetch(this.client._buildURL('ai/guest/usage'), {
412
+ method: 'GET',
413
+ headers,
414
+ });
415
+
416
+ if (!response.ok) {
417
+ const error = await response.json().catch(() => ({ message: response.statusText }));
418
+ throw new Error(error.message || error.error || `API error: ${response.status}`);
419
+ }
420
+
421
+ return await response.json();
422
+ }
423
+
424
+ /**
425
+ * Get cache statistics (admin/monitoring)
426
+ * @returns {Promise<Object>} Cache statistics
427
+ */
428
+ async getCacheStatistics() {
429
+ const data = await this.client.get('ai/guest/cache-stats');
430
+ return data;
431
+ }
184
432
  }
185
433
 
186
434
  export { ChatSession } from './session.js';
@@ -8,6 +8,19 @@ export class ChatSession {
8
8
  * @param {Object} options - Session options
9
9
  * @param {import('../client.js').OblienClient} options.client - Oblien client instance
10
10
  * @param {string} [options.sessionId] - Existing session ID
11
+ */
12
+ constructor({ client, sessionId = null }) {
13
+ if (!client) {
14
+ throw new Error('Oblien client is required');
15
+ }
16
+
17
+ this.client = client;
18
+ this.sessionId = sessionId;
19
+ }
20
+
21
+ /**
22
+ * Create session and get token for client
23
+ * @param {Object} options - Session creation options
11
24
  * @param {string} options.agentId - Agent ID to chat with
12
25
  * @param {string} [options.workflowId] - Workflow ID (if using workflow)
13
26
  * @param {boolean} [options.isGuest] - Is this a guest session
@@ -17,71 +30,30 @@ export class ChatSession {
17
30
  * @param {string} [options.userAgent] - User agent of the user
18
31
  * @param {string} [options.fingerprint] - Fingerprint of the user
19
32
  * @param {string} [options.endUserId] - End user ID (for client's end users)
33
+ * @returns {Promise<Object>} Session data with token for browser
20
34
  */
21
- constructor(options) {
22
- if (!options.client) {
23
- throw new Error('Oblien client is required');
24
- }
25
-
35
+ async create(options) {
26
36
  if (!options.agentId && !options.workflowId) {
27
37
  throw new Error('Either agentId or workflowId is required');
28
38
  }
29
39
 
30
- this.client = options.client;
31
- this.sessionId = options.sessionId || null;
32
- this.agentId = options.agentId;
33
- this.workflowId = options.workflowId;
34
- this.isGuest = options.isGuest || false;
35
- this.namespace = options.namespace;
36
- this.workspace = options.workspace;
37
- this.token = null;
38
- this.data = null;
39
- this.ipAddress = options.ipAddress || null;
40
- this.userAgent = options.userAgent || null;
41
- this.fingerprint = options.fingerprint || null;
42
- this.endUserId = options.endUserId || null;
43
- }
44
-
45
- /**
46
- * Create session and get token for client
47
- * @returns {Promise<Object>} Session data with token for browser
48
- */
49
- async create() {
50
- const payload = {
51
- agent_id: this.agentId,
52
- app_id: this.workflowId, // Backend uses app_id for workflow_id
53
- is_guest: this.isGuest,
54
- namespace: this.namespace,
55
- workspace: this.workspace,
56
- ip_address: this.ipAddress,
57
- user_agent: this.userAgent,
58
- fingerprint: this.fingerprint,
59
- end_user_id: this.endUserId,
60
- };
61
-
62
- this.data = await this.client.post('ai/session/create', payload);
63
- this.sessionId = this.data.sessionId || this.data.session_id;
64
- this.token = this.data.token || this.data.accessToken || this.data.tokens?.token;
65
-
66
- return {
67
- sessionId: this.sessionId,
68
- token: this.token,
69
- agentId: this.data.agentId || this.agentId,
70
- workflowId: this.data.workflowId || this.data.appId || this.workflowId,
71
- namespace: this.namespace,
72
- };
40
+ const data = await this.client.post('ai/session/create', options);
41
+ return data
73
42
  }
74
43
 
75
44
  /**
76
45
  * Get existing session info
77
- * @param {string} sessionId - Session ID
46
+ * @param {string} [sessionId] - Session ID (uses instance sessionId if not provided)
78
47
  * @returns {Promise<Object>} Session details
79
48
  */
80
49
  async get(sessionId) {
81
- this.data = await this.client.get(`ai/session/${sessionId || this.sessionId}`);
82
- return this.data;
83
- }
50
+ const id = sessionId || this.sessionId;
51
+ if (!id) {
52
+ throw new Error('Session ID required');
53
+ }
84
54
 
55
+ return await this.client.get(`ai/session/${id}`);
56
+ }
85
57
 
86
58
  /**
87
59
  * Delete session
@@ -100,12 +72,21 @@ export class ChatSession {
100
72
  /**
101
73
  * List all sessions
102
74
  * @param {Object} [options] - Query options
75
+ * @param {string} [options.namespace] - Filter by namespace
76
+ * @param {string} [options.agentId] - Filter by agent ID
77
+ * @param {string} [options.endUserId] - Filter by end user ID
78
+ * @param {number} [options.limit] - Number of results (max 100)
79
+ * @param {number} [options.offset] - Offset for pagination
80
+ * @param {string} [options.search] - Search in title and user ID
81
+ * @param {string} [options.sortBy] - Sort by 'time' or 'tokens'
82
+ * @param {string} [options.sortOrder] - 'asc' or 'desc'
83
+ * @param {boolean} [options.includeStats] - Include message count and tokens
103
84
  * @returns {Promise<Array>} Array of sessions
104
85
  */
105
86
  async list(options = {}) {
106
- const data = await this.client.get('ai/session', options);
107
- return data.sessions || data;
87
+ const data = await this.client.get('ai/session/list', options);
88
+ return data;
108
89
  }
109
90
  }
110
91
 
111
- export default ChatSession;
92
+ export default ChatSession;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Session Quota Manager
3
+ * Manages per-session quota limits and tracking
4
+ */
5
+
6
+ export class SessionQuota {
7
+ /**
8
+ * @param {Object} options
9
+ * @param {import('../client.js').OblienClient} options.client - Oblien client instance
10
+ * @param {string} options.sessionId - Session ID
11
+ */
12
+ constructor({ client, sessionId }) {
13
+ if (!client) {
14
+ throw new Error('Oblien client is required');
15
+ }
16
+ if (!sessionId) {
17
+ throw new Error('Session ID is required');
18
+ }
19
+
20
+ this.client = client;
21
+ this.sessionId = sessionId;
22
+ }
23
+
24
+ /**
25
+ * Get session quota status
26
+ * @returns {Promise<Object>} Quota status
27
+ */
28
+ async get() {
29
+ const data = await this.client.get(`ai/session/${this.sessionId}/quota`);
30
+ return data.quota || data;
31
+ }
32
+
33
+ /**
34
+ * Set session quota limit (manual override)
35
+ * @param {number} limit - Quota limit (null for unlimited)
36
+ * @param {string} [period] - Quota period: 'daily', 'monthly', 'unlimited'
37
+ * @returns {Promise<Object>} Updated quota status
38
+ */
39
+ async set(limit, period = 'unlimited') {
40
+ const data = await this.client.post(`ai/session/${this.sessionId}/quota`, {
41
+ limit,
42
+ period
43
+ });
44
+ return data.quota || data;
45
+ }
46
+
47
+ /**
48
+ * Reset session quota usage
49
+ * @returns {Promise<Object>} Reset result
50
+ */
51
+ async reset() {
52
+ return await this.client.post(`ai/session/${this.sessionId}/quota/reset`, {});
53
+ }
54
+
55
+ /**
56
+ * Check if session can use credits (validation before action)
57
+ * @param {number} amount - Amount of credits to check
58
+ * @returns {Promise<Object>} { canUse, quota, reason }
59
+ */
60
+ async canUse(amount) {
61
+ const data = await this.client.post(`ai/session/${this.sessionId}/quota/can-use`, {
62
+ amount
63
+ });
64
+ return {
65
+ canUse: data.canUse,
66
+ quota: data.quota,
67
+ reason: data.reason
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Get quota transaction history
73
+ * @param {Object} [options] - Query options
74
+ * @param {number} [options.limit] - Number of results (max 100)
75
+ * @param {number} [options.offset] - Offset for pagination
76
+ * @returns {Promise<Array>} Array of transactions
77
+ */
78
+ async getTransactions(options = {}) {
79
+ const data = await this.client.get(`ai/session/${this.sessionId}/quota/transactions`, options);
80
+ return data.transactions || data;
81
+ }
82
+
83
+ /**
84
+ * Get current usage
85
+ * @returns {Promise<Object>} { used, limit, available }
86
+ */
87
+ async getUsage() {
88
+ const quota = await this.get();
89
+ return {
90
+ used: quota.used || 0,
91
+ limit: quota.limit,
92
+ available: quota.available,
93
+ enabled: quota.enabled
94
+ };
95
+ }
96
+ }
97
+
98
+ export default SessionQuota;
99
+