@@ -3,28 +3,28 @@ import {
3
3
Notice ,
4
4
Modal ,
5
5
App ,
6
- TFolder ,
7
6
Setting ,
8
7
PluginSettingTab ,
9
8
Platform ,
9
+ DropdownComponent ,
10
+ FileSystemAdapter ,
10
11
} from "obsidian" ;
11
12
import { exec , ChildProcess , spawn } from "child_process" ;
12
- import * as path from "path" ;
13
13
import * as os from "os" ;
14
14
import * as fs from "fs/promises" ;
15
15
16
16
interface OpenInterpreterSettings {
17
- apiKey : string ;
18
- anthropicApiKey : string ; // New field for Anthropic API Key
19
- provider : ' OpenAI' | ' Anthropic' ; // New field for Provider selection
20
- model : string ; // New field for LLM Model selection
17
+ openaiApiKey : string ;
18
+ anthropicApiKey : string ;
19
+ provider : " OpenAI" | " Anthropic" ;
20
+ model : string ;
21
21
}
22
22
23
23
const DEFAULT_SETTINGS : OpenInterpreterSettings = {
24
- apiKey : "" ,
25
- anthropicApiKey : "" , // Default value for Anthropic API Key
26
- provider : ' OpenAI' , // Default provider
27
- model : ' gpt-3.5-turbo' , // Default model
24
+ openaiApiKey : "" ,
25
+ anthropicApiKey : "" ,
26
+ provider : " OpenAI" , // Default provider
27
+ model : " gpt-4o" , // Default model
28
28
} ;
29
29
30
30
class InstallationGuideModal extends Modal {
@@ -277,40 +277,10 @@ export default class OpenInterpreterPlugin extends Plugin {
277
277
} ) ;
278
278
}
279
279
280
- private async getInterpreterProfilePath ( ) : Promise < string > {
281
- const homedir = os . homedir ( ) ;
282
- const profileDir = path . join (
283
- homedir ,
284
- "Library" ,
285
- "Application Support" ,
286
- "open-interpreter" ,
287
- "profiles"
288
- ) ;
289
- const profilePath = path . join ( profileDir , "obsidian.py" ) ;
290
-
291
- // Ensure the directory exists
292
- await fs . mkdir ( profileDir , { recursive : true } ) ;
293
-
294
- // Create an empty profile file if it doesn't exist
295
- if ( ! ( await fs . stat ( profilePath ) . catch ( ( ) => false ) ) ) {
296
- await fs . writeFile (
297
- profilePath ,
298
- "# Obsidian profile for Open Interpreter\n"
299
- ) ;
300
- }
301
-
302
- return profilePath ;
303
- }
304
-
305
280
private getVaultPath ( ) : string | null {
306
281
const adapter = this . app . vault . adapter ;
307
- if ( adapter && "basePath" in adapter ) {
308
- return ( adapter as any ) . basePath ;
309
- }
310
- // Fallback to the previous method if basePath is not available
311
- const rootFolder = this . app . vault . getRoot ( ) ;
312
- if ( rootFolder instanceof TFolder ) {
313
- return rootFolder . path ;
282
+ if ( adapter instanceof FileSystemAdapter ) {
283
+ return adapter . getBasePath ( ) ;
314
284
}
315
285
console . error ( "Could not determine vault path" ) ;
316
286
return null ;
@@ -330,9 +300,10 @@ export default class OpenInterpreterPlugin extends Plugin {
330
300
}
331
301
332
302
private async executeInterpreterCommand ( command : string ) {
333
- const profilePath = await this . getInterpreterProfilePath ( ) ;
334
303
const vaultPath = this . getVaultPath ( ) ;
335
304
305
+ console . log ( "Determined vault path:" , vaultPath ) ;
306
+
336
307
if ( ! vaultPath ) {
337
308
console . error ( "Vault path could not be determined." ) ;
338
309
new Notice (
@@ -350,28 +321,66 @@ export default class OpenInterpreterPlugin extends Plugin {
350
321
}
351
322
352
323
const env = { ...process . env } ;
353
- if ( this . settings . provider === 'OpenAI' ) {
354
- env . OPENAI_API_KEY = this . settings . apiKey ;
355
- } else if ( this . settings . provider === 'Anthropic' ) {
356
- env . ANTHROPIC_API_KEY = this . settings . anthropicApiKey ;
324
+ if ( this . settings . provider === "OpenAI" ) {
325
+ env . OPENAI_API_KEY =
326
+ process . env . OPENAI_API_KEY || this . settings . openaiApiKey ;
327
+ } else if ( this . settings . provider === "Anthropic" ) {
328
+ env . ANTHROPIC_API_KEY =
329
+ process . env . ANTHROPIC_API_KEY || this . settings . anthropicApiKey ;
330
+ }
331
+
332
+ // Check if the API key is set
333
+ const apiKey =
334
+ this . settings . provider === "OpenAI"
335
+ ? env . OPENAI_API_KEY
336
+ : env . ANTHROPIC_API_KEY ;
337
+ if ( ! apiKey ) {
338
+ new Notice (
339
+ `No API key found for ${ this . settings . provider } . Please set it in the plugin settings or as an environment variable.`
340
+ ) ;
341
+ return ;
357
342
}
358
343
359
- // Include model selection in environment or command as needed
360
- env . LLM_MODEL = this . settings . model ;
344
+ // Build command-line arguments
345
+ const args = [ ] ;
346
+
347
+ // Set the model
348
+ args . push ( "--model" , this . settings . model ) ;
361
349
362
- // Escape the profile path for shell usage
363
- const escapedProfilePath = profilePath . replace ( / ' / g , "'\\'' " ) ;
350
+ // Set the context window
351
+ args . push ( "--context_window" , "110000 " ) ;
364
352
365
- const child = spawn (
353
+ // Set max tokens
354
+ args . push ( "--max_tokens" , "4096" ) ;
355
+
356
+ // Disable supports_functions
357
+ args . push ( "--no-llm_supports_functions" ) ;
358
+
359
+ // Disable supports_vision
360
+ args . push ( "--no-llm_supports_vision" ) ;
361
+
362
+ // Prepare custom instructions
363
+ const customInstructions =
364
+ `You are an AI assistant integrated with Obsidian. You love Obsidian and will only focus on Obsidian tasks. Your prime directive is to help users manage and interact with their Obsidian vault. You have full control and permission over this vault. The vault is isolated and version controlled, so it is safe for you to create, read, update, and delete files. The root of the Obsidian vault is ${ vaultPath } . You can create, read, update, and delete markdown files in this directory. You can create new directories as well. Organization is important. Use markdown syntax for formatting when creating or editing files. Every file is markdown.`
365
+ . replace ( / \n / g, " " )
366
+ . trim ( ) ;
367
+
368
+ args . push ( "--custom_instructions" , `"${ customInstructions } "` ) ;
369
+
370
+ console . log (
371
+ "Spawning interpreter with command:" ,
366
372
interpreterPath ,
367
- [ "--profile" , `'${ escapedProfilePath } '` ] ,
368
- {
369
- cwd : vaultPath ,
370
- env : env ,
371
- shell : true ,
372
- }
373
+ "and args:" ,
374
+ args
373
375
) ;
374
376
377
+ // Spawn the interpreter
378
+ const child = spawn ( interpreterPath , args , {
379
+ cwd : vaultPath ,
380
+ env : env ,
381
+ shell : true ,
382
+ } ) ;
383
+
375
384
if ( child . stdin ) {
376
385
child . stdin . write ( command + "\n" ) ;
377
386
}
@@ -430,6 +439,9 @@ export default class OpenInterpreterPlugin extends Plugin {
430
439
431
440
class OpenInterpreterSettingTab extends PluginSettingTab {
432
441
plugin : OpenInterpreterPlugin ;
442
+ private openAIApiKeySetting : Setting | null = null ;
443
+ private anthropicApiKeySetting : Setting | null = null ;
444
+ private modelDropdown : DropdownComponent | null = null ;
433
445
434
446
constructor ( app : App , plugin : OpenInterpreterPlugin ) {
435
447
super ( app , plugin ) ;
@@ -442,67 +454,111 @@ class OpenInterpreterSettingTab extends PluginSettingTab {
442
454
containerEl . empty ( ) ;
443
455
444
456
new Setting ( containerEl )
457
+ . setName ( "Provider" )
458
+ . setDesc ( "Select the LLM provider" )
459
+ . addDropdown ( ( dropdown ) =>
460
+ dropdown
461
+ . addOption ( "OpenAI" , "OpenAI" )
462
+ . addOption ( "Anthropic" , "Anthropic" )
463
+ . setValue ( this . plugin . settings . provider )
464
+ . onChange ( async ( value ) => {
465
+ this . plugin . settings . provider = value as "OpenAI" | "Anthropic" ;
466
+ await this . plugin . saveSettings ( ) ;
467
+ this . updateApiKeyVisibility ( ) ;
468
+ this . updateModelOptions ( ) ;
469
+ } )
470
+ ) ;
471
+
472
+ this . openAIApiKeySetting = new Setting ( containerEl )
445
473
. setName ( "OpenAI API Key" )
446
474
. setDesc ( "Enter your OpenAI API key" )
447
475
. addText ( ( text ) =>
448
476
text
449
477
. setPlaceholder ( "Enter your OpenAI API key" )
450
- . setValue ( this . plugin . settings . apiKey )
478
+ . setValue ( this . plugin . settings . openaiApiKey )
451
479
. onChange ( async ( value ) => {
452
- this . plugin . settings . apiKey = value ;
480
+ this . plugin . settings . openaiApiKey = value ;
453
481
await this . plugin . saveSettings ( ) ;
454
482
} )
455
483
) ;
456
484
457
- new Setting ( containerEl )
458
- . setName ( "Provider" )
459
- . setDesc ( "Select the LLM provider" )
460
- . addDropdown ( ( dropdown ) =>
461
- dropdown
462
- . addOption ( "OpenAI" , "OpenAI" )
463
- . addOption ( "Anthropic" , "Anthropic" )
464
- . setValue ( this . plugin . settings . provider )
485
+ this . anthropicApiKeySetting = new Setting ( containerEl )
486
+ . setName ( "Anthropic API Key" )
487
+ . setDesc ( "Enter your Anthropic API key" )
488
+ . addText ( ( text ) =>
489
+ text
490
+ . setPlaceholder ( "Enter your Anthropic API key" )
491
+ . setValue ( this . plugin . settings . anthropicApiKey )
465
492
. onChange ( async ( value ) => {
466
- this . plugin . settings . provider = value as 'OpenAI' | 'Anthropic' ;
493
+ this . plugin . settings . anthropicApiKey = value ;
467
494
await this . plugin . saveSettings ( ) ;
468
- this . display ( ) ; // Refresh the settings to show/hide relevant fields
469
495
} )
470
496
) ;
471
497
472
- if ( this . plugin . settings . provider === 'Anthropic' ) {
473
- new Setting ( containerEl )
474
- . setName ( "Anthropic API Key" )
475
- . setDesc ( "Enter your Anthropic API key" )
476
- . addText ( ( text ) =>
477
- text
478
- . setPlaceholder ( "Enter your Anthropic API key" )
479
- . setValue ( this . plugin . settings . anthropicApiKey )
480
- . onChange ( async ( value ) => {
481
- this . plugin . settings . anthropicApiKey = value ;
482
- await this . plugin . saveSettings ( ) ;
483
- } )
484
- ) ;
485
- }
486
-
487
498
new Setting ( containerEl )
488
499
. setName ( "Model" )
489
500
. setDesc ( "Select the LLM model" )
490
501
. addDropdown ( ( dropdown ) => {
491
- const models = this . plugin . settings . provider === 'OpenAI'
492
- ? {
493
- "gpt-3.5-turbo" : "GPT-3.5 Turbo" ,
494
- "gpt-4" : "GPT-4" ,
495
- }
496
- : {
497
- "claude-v1" : "Claude v1" ,
498
- "claude-v2" : "Claude v2" ,
499
- } ;
500
- dropdown . addOptions ( models ) ;
501
- dropdown . setValue ( this . plugin . settings . model ) ;
502
+ this . modelDropdown = dropdown ;
503
+ this . updateModelOptions ( ) ;
502
504
dropdown . onChange ( async ( value ) => {
503
505
this . plugin . settings . model = value ;
504
506
await this . plugin . saveSettings ( ) ;
505
507
} ) ;
506
508
} ) ;
509
+
510
+ this . updateApiKeyVisibility ( ) ;
511
+ }
512
+
513
+ private updateApiKeyVisibility ( ) {
514
+ if ( this . openAIApiKeySetting && this . anthropicApiKeySetting ) {
515
+ if ( this . plugin . settings . provider === "OpenAI" ) {
516
+ this . openAIApiKeySetting . settingEl . style . display = "block" ;
517
+ this . anthropicApiKeySetting . settingEl . style . display = "none" ;
518
+ } else {
519
+ this . openAIApiKeySetting . settingEl . style . display = "none" ;
520
+ this . anthropicApiKeySetting . settingEl . style . display = "block" ;
521
+ }
522
+ }
523
+ }
524
+
525
+ private updateModelOptions ( ) {
526
+ if ( this . modelDropdown ) {
527
+ const models = this . getModelsForProvider ( this . plugin . settings . provider ) ;
528
+ // Remove all existing options
529
+ this . modelDropdown . selectEl . empty ( ) ;
530
+ // Add new options
531
+ Object . entries ( models ) . forEach ( ( [ value , name ] ) => {
532
+ this . modelDropdown ?. addOption ( value , name ) ;
533
+ } ) ;
534
+
535
+ // Set the first model as default if the current model is not in the list
536
+ if ( ! models [ this . plugin . settings . model ] ) {
537
+ this . plugin . settings . model = Object . keys ( models ) [ 0 ] ;
538
+ this . plugin . saveSettings ( ) ;
539
+ }
540
+
541
+ this . modelDropdown . setValue ( this . plugin . settings . model ) ;
542
+ }
543
+ }
544
+
545
+ private getModelsForProvider (
546
+ provider : "OpenAI" | "Anthropic"
547
+ ) : Record < string , string > {
548
+ switch ( provider ) {
549
+ case "OpenAI" :
550
+ return {
551
+ "gpt-4o" : "GPT-4o" ,
552
+ "gpt-4o-mini" : "GPT-4o-mini" ,
553
+ } ;
554
+ case "Anthropic" :
555
+ return {
556
+ "claude-3-5-sonnet-20241022" : "Claude 3.5 Sonnet" ,
557
+ "claude-3-opus-20240229" : "Claude 3 Opus" ,
558
+ } ;
559
+ default :
560
+ console . error ( `Unknown provider: ${ provider } ` ) ;
561
+ return { } ;
562
+ }
507
563
}
508
564
}
0 commit comments