2
2
3
3
import fs from 'fs' ;
4
4
import path from 'path' ;
5
- import { TrieveSDK } from 'trieve-ts-sdk' ;
5
+ import { TrieveSDK , Topic } from 'trieve-ts-sdk' ;
6
6
import { program } from 'commander' ;
7
7
import chalk from 'chalk' ;
8
8
import inquirer from 'inquirer' ;
9
9
import Conf from 'conf' ;
10
+ import os from 'os' ;
10
11
11
- // Use XDG_CONFIG_HOME or default to ~/.config
12
- const configDir =
13
- process . env . XDG_CONFIG_HOME || `${ require ( 'os' ) . homedir ( ) } /.config` ;
12
+ interface UploadedFile {
13
+ fileName: string ;
14
+ filePath: string ;
15
+ trackingId: string ;
16
+ uploadedAt: string ;
17
+ status: 'pending' | 'completed' ;
18
+ }
19
+
20
+ const configDir = process . env . XDG_CONFIG_HOME || `${ os . homedir ( ) } /.config` ;
14
21
const config = new Conf ( {
15
22
cwd : `${ configDir } /trieve-cli` ,
16
23
configName : 'config' ,
@@ -22,14 +29,8 @@ const uploadedFilesPath = path.join(
22
29
'uploaded_files.json' ,
23
30
) ;
24
31
25
- // File tracking interface
26
- interface UploadedFile {
27
- fileName: string ;
28
- filePath: string ;
29
- trackingId: string ;
30
- uploadedAt: string ;
31
- status ? : 'pending' | 'completed' ;
32
- }
32
+ // Path for storing topics data
33
+ const topicsPath = path . join ( `${ configDir } /trieve-cli` , 'topics.json' ) ;
33
34
34
35
// Function to manage uploaded files tracking
35
36
function manageUploadedFiles (
@@ -68,8 +69,39 @@ function manageUploadedFiles(
68
69
}
69
70
}
70
71
71
- function getConfigOrEnv ( key : string , envVar : string ) {
72
- return process . env [ envVar ] || config . get ( key ) ;
72
+ // Function to manage topics
73
+ function manageTopics ( action : 'get' | 'add' , topicData ?: Topic ) : Topic [ ] {
74
+ try {
75
+ // Create the directory if it doesn't exist
76
+ if ( ! fs . existsSync ( path . dirname ( topicsPath ) ) ) {
77
+ fs . mkdirSync ( path . dirname ( topicsPath ) , { recursive : true } ) ;
78
+ }
79
+
80
+ // Initialize or read existing data
81
+ let topics : Topic [ ] = [ ] ;
82
+ if ( fs . existsSync ( topicsPath ) ) {
83
+ const fileContent = fs . readFileSync ( topicsPath , 'utf-8' ) ;
84
+ topics = fileContent ? JSON . parse ( fileContent ) : [ ] ;
85
+ }
86
+
87
+ if ( action === 'add' && topicData ) {
88
+ // Add new topic data
89
+ topics. push ( topicData ) ;
90
+ fs . writeFileSync ( topicsPath , JSON . stringify ( topics , null , 2 ) ) ;
91
+ }
92
+
93
+ return topics ;
94
+ } catch ( error ) {
95
+ console . error (
96
+ chalk . red ( '❌ Error managing topics:' ) ,
97
+ error instanceof Error ? error . message : error ,
98
+ ) ;
99
+ return [ ] ;
100
+ }
101
+ }
102
+
103
+ function getConfigOrEnv ( key : string , envVar : string ) : string | undefined {
104
+ return process . env [ envVar ] || ( config . get ( key ) as string ) ;
73
105
}
74
106
75
107
function ensureTrieveConfig ( ) {
@@ -180,30 +212,25 @@ async function promptForFile() {
180
212
}
181
213
}
182
214
183
- // Function to check file upload status using tracking ID
184
- async function checkFileUploadStatus ( trackingId : string ) : Promise < boolean > {
215
+ async function checkFileUploadStatus (
216
+ groupTrackingId : string ,
217
+ ) : Promise < boolean > {
185
218
try {
186
219
const { apiKey, datasetId } = ensureTrieveConfig ( ) ;
187
220
const trieveClient : TrieveSDK = new TrieveSDK ( {
188
221
apiKey,
189
222
datasetId,
190
223
} ) ;
191
224
192
- // Call the API to check the status
193
- const response = await trieveClient . getChunksGroupByTrackingId (
194
- trackingId ,
195
- 1 ,
196
- ) ;
225
+ const response = await trieveClient . getChunksGroupByTrackingId ( {
226
+ groupTrackingId,
227
+ page : 1 ,
228
+ } ) ;
197
229
198
- // If we get chunks back, the file is processed
199
230
const isCompleted = response . chunks && response . chunks . length > 0 ;
200
231
201
232
return isCompleted ;
202
233
} catch ( error ) {
203
- console . error (
204
- chalk . red ( `❌ Failed to check status for tracking ID ${ trackingId } :` ) ,
205
- error instanceof Error ? error . message : error ,
206
- ) ;
207
234
return false ;
208
235
}
209
236
}
@@ -327,6 +354,80 @@ async function interactiveCheckStatus(): Promise<void> {
327
354
}
328
355
}
329
356
357
+ // Function to ask a question and stream back the response
358
+ async function askQuestion ( question : string ) : Promise < void > {
359
+ try {
360
+ console . log ( chalk . blue ( '🤔 Processing your question...' ) ) ;
361
+
362
+ const { apiKey , datasetId } = ensureTrieveConfig ( ) ;
363
+ const trieveClient : TrieveSDK = new TrieveSDK ( {
364
+ apiKey,
365
+ datasetId,
366
+ } ) ;
367
+
368
+ // Generate a topic name from the question (use first few words)
369
+ const topicName = question . split ( ' ' ) . slice ( 0 , 5 ) . join ( ' ' ) + '...' ;
370
+
371
+ // Create a topic
372
+ console . log ( chalk . blue ( '📝 Creating a new topic...' ) ) ;
373
+ const ownerId =
374
+ ( config . get ( 'userId' ) as string ) ||
375
+ 'default-user-' + Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) ;
376
+
377
+ const topicData = await trieveClient . createTopic ( {
378
+ first_user_message : question ,
379
+ name : topicName ,
380
+ owner_id : ownerId ,
381
+ } ) ;
382
+
383
+ // Save the topic for future reference
384
+ manageTopics ( 'add' , topicData as Topic ) ;
385
+
386
+ console . log ( chalk . green ( `✅ Topic created: ${ topicName } ` ) ) ;
387
+ console . log ( chalk . blue ( '🔍 Fetching answer...' ) ) ;
388
+
389
+ // Create a message and stream the response
390
+ const { reader, queryId } =
391
+ await trieveClient . createMessageReaderWithQueryId ( {
392
+ topic_id : topicData . id ,
393
+ new_message_content : question ,
394
+ } ) ;
395
+
396
+ console . log ( chalk . yellow ( '\n🤖 Answer:' ) ) ;
397
+ console . log ( '─' . repeat ( 80 ) ) ;
398
+
399
+ // Stream the response
400
+ const decoder = new TextDecoder ( ) ;
401
+ let answer = '' ;
402
+
403
+ try {
404
+ while ( true ) {
405
+ const { done, value } = await reader . read ( ) ;
406
+ if ( done ) break ;
407
+
408
+ const chunk = decoder . decode ( value ) ;
409
+ answer += chunk ;
410
+ process . stdout . write ( chunk ) ;
411
+ }
412
+ } catch ( e ) {
413
+ console . error ( chalk . red ( '❌ Error streaming response:' ) , e ) ;
414
+ } finally {
415
+ reader. releaseLock ( ) ;
416
+ }
417
+
418
+ console . log ( '\n' + '─' . repeat ( 80 ) ) ;
419
+ console . log ( chalk . green ( '✅ Response complete' ) ) ;
420
+ console . log (
421
+ chalk . blue ( `📚 Topic ID: ${ topicData . id } (saved for future reference)` ) ,
422
+ ) ;
423
+ } catch ( error ) {
424
+ console . error (
425
+ chalk . red ( '❌ Failed to process question:' ) ,
426
+ error instanceof Error ? error . message : error ,
427
+ ) ;
428
+ }
429
+ }
430
+
330
431
program
331
432
. name ( 'trieve-cli' )
332
433
. description ( 'A CLI tool for using Trieve' )
@@ -362,9 +463,16 @@ program
362
463
message : 'Enter your TRIEVE_DATASET_ID:' ,
363
464
default : ( config . get ( 'TRIEVE_DATASET_ID' ) as string ) || '' ,
364
465
} ,
466
+ {
467
+ type : 'input' ,
468
+ name : 'userId' ,
469
+ message : 'Enter your user ID (for topic ownership):' ,
470
+ default : ( config . get ( 'userId' ) as string ) || '' ,
471
+ } ,
365
472
] ) ;
366
473
config . set ( 'TRIEVE_API_KEY' , answers . TRIEVE_API_KEY ) ;
367
474
config . set ( 'TRIEVE_DATASET_ID' , answers . TRIEVE_DATASET_ID ) ;
475
+ config . set ( 'userId' , answers . userId ) ;
368
476
console . log ( chalk . green ( '✅ Configuration saved!' ) ) ;
369
477
} ) ;
370
478
@@ -386,6 +494,33 @@ program
386
494
}
387
495
} ) ;
388
496
497
+ program
498
+ . command ( 'ask' )
499
+ . description ( 'Ask a question and get a streamed response' )
500
+ . argument ( '<question>' , 'The question to ask' )
501
+ . action ( async ( question ) => {
502
+ await askQuestion ( question ) ;
503
+ } ) ;
504
+
505
+ program
506
+ . command ( 'interactive-ask' )
507
+ . description ( 'Ask a question interactively and get a streamed response' )
508
+ . action ( async ( ) => {
509
+ const answers = await inquirer . prompt ( [
510
+ {
511
+ type : 'input' ,
512
+ name : 'question' ,
513
+ message : 'What would you like to ask?' ,
514
+ validate : ( input ) => {
515
+ if ( ! input ) return 'Question is required' ;
516
+ return true ;
517
+ } ,
518
+ } ,
519
+ ] ) ;
520
+
521
+ await askQuestion ( answers . question ) ;
522
+ } ) ;
523
+
389
524
program . addHelpText (
390
525
'after' ,
391
526
`
@@ -395,6 +530,8 @@ ${chalk.yellow('Examples:')}
395
530
$ ${ chalk . green ( 'trieve-cli check-upload-status' ) }
396
531
$ ${ chalk . green ( 'trieve-cli check-upload-status --all' ) }
397
532
$ ${ chalk . green ( 'trieve-cli check-upload-status --tracking-id <tracking-id>' ) }
533
+ $ ${ chalk . green ( 'trieve-cli ask "What is the capital of France?"' ) }
534
+ $ ${ chalk . green ( 'trieve-cli interactive-ask' ) }
398
535
` ,
399
536
) ;
400
537
0 commit comments