Skip to content

Commit fafce47

Browse files
Merge pull request #68 from SmythOS/dev
Dev merge
2 parents a1d44bf + 686ced8 commit fafce47

File tree

11 files changed

+259
-67
lines changed

11 files changed

+259
-67
lines changed

examples/01-agent-code-skill/01-prompting.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Agent, Scope } from '@smythos/sdk';
33
async function main() {
44
const agent = new Agent({
55
id: 'crypto-market-assistant',
6-
6+
77
name: 'CryptoMarket Assistant',
88
behavior: 'You are a crypto price tracker. You are given a coin id and you need to get the price of the coin in USD',
99
model: 'gpt-4o',
@@ -19,28 +19,6 @@ async function main() {
1919
return data.market_data;
2020
},
2121
});
22-
const imgSkill = agent.addSkill({
23-
name: 'ImageAnalyser',
24-
description: 'Any attachment should be processed by this function',
25-
process: async ({ image_url }) => {
26-
console.log(image_url);
27-
return 'Image analysed';
28-
},
29-
});
30-
// imgSkill.in({
31-
// image_url : {
32-
// type:'Binary',
33-
// description:'The image url that we will analyze'
34-
// }
35-
// })
36-
37-
38-
39-
const prompt = await agent.prompt('Analyze this image please ', {
40-
files: ['https://www.robuxio.com/wp-content/uploads/2023/05/Trend.png'],
41-
});
42-
console.log(prompt);
43-
4422

4523
//this will prompt the agent and use the agent's LLM to determine which skill to use
4624
const promptResult = await agent.prompt('What are the current prices of Bitcoin and Ethereum ?');
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Agent, Scope } from '@smythos/sdk';
2+
3+
async function main() {
4+
const agent = new Agent({
5+
id: 'image-analyser',
6+
7+
name: 'Image Analyser',
8+
behavior: 'You are a image analyser. You are given a image url and you need to analyse the image',
9+
model: 'gpt-4o',
10+
});
11+
12+
const imgSkill = agent.addSkill({
13+
name: 'ImageAnalyser',
14+
description: 'Any attachment should be processed by this function',
15+
process: async ({ image_url }) => {
16+
console.log(image_url);
17+
return 'Image analysed';
18+
},
19+
});
20+
imgSkill.in({
21+
image_url: {
22+
type: 'Binary',
23+
description: 'The image url that we will analyze',
24+
},
25+
});
26+
27+
const promptResult = await agent.prompt('Analyze this image please ', {
28+
files: ['https://www.robuxio.com/wp-content/uploads/2023/05/Trend.png'],
29+
});
30+
console.log(promptResult);
31+
}
32+
33+
main();

examples/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@types/node": "^20.19.0",
2020
"ts-node": "^10.9.2",
2121
"tsx": "^4.7.0",
22-
"typescript": "^5.3.0"
22+
"typescript": "^5.3.0",
23+
"why-is-node-running": "^3.2.2"
2324
}
2425
}

packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import crypto from 'crypto';
1313
import fs from 'fs';
1414
import * as readlineSync from 'readline-sync';
1515
import path from 'path';
16+
import * as chokidar from 'chokidar';
1617
import { findSmythPath } from '../../../../helpers/Sysconfig.helper';
1718

1819
const console = Logger('JSONFileVault');
@@ -28,55 +29,18 @@ export class JSONFileVault extends VaultConnector {
2829
private vaultData: any;
2930
private index: any;
3031
private shared: string;
32+
private vaultFile: string;
33+
private watcher: chokidar.FSWatcher | null = null;
3134

3235
constructor(protected _settings: JSONFileVaultConfig) {
3336
super(_settings);
3437
//if (!SmythRuntime.Instance) throw new Error('SRE not initialized');
3538

3639
this.shared = _settings.shared || ''; //if config.shared, all keys are accessible to all teams, and they are set under the 'shared' teamId
3740

38-
let vaultFile = this.findVaultFile(_settings.file);
39-
this.vaultData = {};
40-
if (fs.existsSync(vaultFile)) {
41-
try {
42-
if (_settings.fileKey && fs.existsSync(_settings.fileKey)) {
43-
try {
44-
const privateKey = fs.readFileSync(_settings.fileKey, 'utf8');
45-
const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString();
46-
const decryptedBuffer = crypto.privateDecrypt(
47-
{
48-
key: privateKey,
49-
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
50-
},
51-
Buffer.from(encryptedVault, 'base64')
52-
);
53-
this.vaultData = JSON.parse(decryptedBuffer.toString('utf8'));
54-
} catch (error) {
55-
throw new Error('Failed to decrypt vault');
56-
}
57-
} else {
58-
this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString());
59-
}
60-
} catch (e) {
61-
console.error('Error parsing vault file:', e);
62-
console.error('!!! Vault features might not work properly !!!');
63-
this.vaultData = {};
64-
}
65-
66-
if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) {
67-
//this is an encrypted vault we need to request the master key
68-
this.setInteraction(this.getMasterKeyInteractive.bind(this));
69-
}
70-
71-
for (let teamId in this.vaultData) {
72-
for (let resourceId in this.vaultData[teamId]) {
73-
if (!this.index) this.index = {};
74-
if (!this.index[resourceId]) this.index[resourceId] = {};
75-
const value = this.vaultData[teamId][resourceId];
76-
this.index[resourceId][teamId] = value;
77-
}
78-
}
79-
}
41+
this.vaultFile = this.findVaultFile(_settings.file);
42+
this.fetchVaultData(this.vaultFile, _settings);
43+
this.initFileWatcher();
8044
}
8145

8246
private findVaultFile(vaultFile) {
@@ -192,4 +156,66 @@ export class JSONFileVault extends VaultConnector {
192156

193157
return acl;
194158
}
159+
160+
private fetchVaultData(vaultFile: string, _settings: JSONFileVaultConfig) {
161+
if (fs.existsSync(vaultFile)) {
162+
try {
163+
if (_settings.fileKey && fs.existsSync(_settings.fileKey)) {
164+
try {
165+
const privateKey = fs.readFileSync(_settings.fileKey, 'utf8');
166+
const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString();
167+
const decryptedBuffer = crypto.privateDecrypt(
168+
{
169+
key: privateKey,
170+
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
171+
},
172+
Buffer.from(encryptedVault, 'base64')
173+
);
174+
this.vaultData = JSON.parse(decryptedBuffer.toString('utf8'));
175+
} catch (error) {
176+
throw new Error('Failed to decrypt vault');
177+
}
178+
} else {
179+
this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString());
180+
}
181+
} catch (e) {
182+
console.error('Error parsing vault file:', e);
183+
console.error('!!! Vault features might not work properly !!!');
184+
this.vaultData = {};
185+
}
186+
187+
if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) {
188+
//this is an encrypted vault we need to request the master key
189+
this.setInteraction(this.getMasterKeyInteractive.bind(this));
190+
}
191+
192+
for (let teamId in this.vaultData) {
193+
for (let resourceId in this.vaultData[teamId]) {
194+
if (!this.index) this.index = {};
195+
if (!this.index[resourceId]) this.index[resourceId] = {};
196+
const value = this.vaultData[teamId][resourceId];
197+
this.index[resourceId][teamId] = value;
198+
}
199+
}
200+
}
201+
}
202+
203+
private initFileWatcher() {
204+
this.watcher = chokidar.watch(this.vaultFile, {
205+
persistent: false, // Don't keep the process running
206+
ignoreInitial: true,
207+
});
208+
209+
this.watcher.on('change', () => {
210+
this.fetchVaultData(this.vaultFile, this._settings);
211+
});
212+
}
213+
214+
public async stop() {
215+
super.stop();
216+
if (this.watcher) {
217+
this.watcher.close();
218+
this.watcher = null;
219+
}
220+
}
195221
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { setupSRE } from '../../utils/sre';
3+
import { ConnectorService } from '@sre/Core/ConnectorsService';
4+
import { IAccessCandidate, TAccessRole } from 'index';
5+
6+
setupSRE({
7+
Vault: {
8+
Connector: 'JSONFileVault',
9+
Settings: {
10+
file: '/Users/zubair/Zubair/SmythOS/smyth-opensource/smythos-ui/vault.json',
11+
},
12+
},
13+
Log: {
14+
Connector: 'ConsoleLog',
15+
},
16+
});
17+
18+
describe('JSONFileVault Tests', () => {
19+
it(
20+
'List all keys in the vault',
21+
async () => {
22+
const mockCandidate: IAccessCandidate = {
23+
id: 'default',
24+
role: TAccessRole.Team,
25+
};
26+
27+
const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault');
28+
const result = await vaultConnector.team(mockCandidate.id).listKeys();
29+
expect(result).toBeDefined();
30+
},
31+
);
32+
33+
it(
34+
'Get a key from the vault',
35+
async () => {
36+
const mockCandidate: IAccessCandidate = {
37+
id: 'default',
38+
role: TAccessRole.Team,
39+
};
40+
41+
const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault');
42+
const result = await vaultConnector.team(mockCandidate.id).get('testKey');
43+
expect(result).toBe('testValue');
44+
},
45+
);
46+
});

packages/sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@smythos/sdk",
3-
"version": "1.0.33",
3+
"version": "1.0.34",
44
"description": "SRE SDK",
55
"keywords": [
66
"smythos",

packages/sdk/src/Agent/Agent.class.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { LLMInstance, TLLMInstanceParams } from '../LLM/LLMInstance.class';
3333
import { AgentData, ChatOptions, Scope } from '../types/SDKTypes';
3434
import { MCP, MCPSettings, MCPTransport } from '../MCP/MCP.class';
3535
import { findClosestModelInfo } from '../LLM/Model';
36+
import { VaultInstance } from '../Vault/VaultInstance.class';
3637

3738
const console = SDKLog;
3839

@@ -577,6 +578,27 @@ export class Agent extends SDKObject {
577578
return this._vectorDBProviders;
578579
}
579580

581+
private _vault: VaultInstance;
582+
583+
/**
584+
* Access to the agent's team vault
585+
*
586+
* This will give you access to the current agent team vault, if no explicit team is set, the agent belongs to "default" team.
587+
*
588+
* @example
589+
* ```typescript
590+
* const value = await agent.vault.get('my-key');
591+
* ```
592+
*
593+
* @returns The vault instance
594+
*/
595+
public get vault() {
596+
if (!this._vault) {
597+
this._vault = new VaultInstance(AccessCandidate.agent(this._data.id));
598+
}
599+
return this._vault;
600+
}
601+
580602
/**
581603
* Add a skill to the agent, enabling it to perform specific tasks or operations.
582604
*

packages/sdk/src/Vault/Vault.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { AccessCandidate, ConnectorService } from '@smythos/sre';
2+
3+
export class Vault {
4+
/**
5+
* Get a value from the vault
6+
* @param key - The key to get the value for
7+
* @param candidate - The candidate to get the value for
8+
* @returns The value of the key
9+
*/
10+
static async get(key: string, candidate?: AccessCandidate) {
11+
if (!candidate) candidate = AccessCandidate.team('default');
12+
const vaultConnector = ConnectorService.getVaultConnector();
13+
const vaultRequester = vaultConnector.requester(candidate);
14+
15+
const value = await vaultRequester.get(key);
16+
return value;
17+
}
18+
19+
/**
20+
* List all keys in the vault
21+
* @param candidate - The candidate to list the keys for
22+
* @returns An array of keys
23+
*/
24+
static async listKeys(candidate?: AccessCandidate) {
25+
if (!candidate) candidate = AccessCandidate.team('default');
26+
27+
const vaultConnector = ConnectorService.getVaultConnector();
28+
const vaultRequester = vaultConnector.requester(candidate);
29+
30+
const keys = await vaultRequester.listKeys();
31+
return keys;
32+
}
33+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { AccessCandidate, ConnectorService, IAccessCandidate, IVaultRequest, VaultConnector } from '@smythos/sre';
2+
import { SRE } from '@smythos/sre';
3+
import { SDKObject } from '../Core/SDKObject.class';
4+
5+
export class VaultInstance extends SDKObject {
6+
#candidate: IAccessCandidate;
7+
#vaultRequester: IVaultRequest;
8+
constructor(candidate?: AccessCandidate) {
9+
super();
10+
this.#candidate = candidate || AccessCandidate.team('default');
11+
}
12+
13+
protected async init() {
14+
//if the SRE instance is not initializing, initialize it with default settings
15+
if (!SRE.initializing) SRE.init({});
16+
await SRE.ready();
17+
const vaultConnector = ConnectorService.getVaultConnector();
18+
this.#vaultRequester = vaultConnector.requester(this.#candidate);
19+
20+
this._readyPromise.resolve(true);
21+
}
22+
23+
/**
24+
* Get a value from the vault
25+
* @param key - The key to get the value for
26+
* @returns The value of the key
27+
*/
28+
public async get(key: string) {
29+
const value = await this.#vaultRequester.get(key);
30+
return value;
31+
}
32+
33+
/**
34+
* List all keys in the vault
35+
* @returns An array of keys
36+
*/
37+
public async listKeys() {
38+
const keys = await this.#vaultRequester.listKeys();
39+
return keys;
40+
}
41+
}

packages/sdk/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export * from './types/ExportedSRETypes';
2929
export * from './types/SDKTypes';
3030
export * from './utils/help';
3131
export * from './utils/index';
32+
export * from './Vault/Vault';
33+
export * from './Vault/VaultInstance.class';
3234
export * from './VectorDB/VectorDB.class';
3335
export * from './VectorDB/VectorDBInstance.class';
3436
export * from './Components/generated/APICall';

0 commit comments

Comments
 (0)