@applica-software-guru/persona-sdk 0.1.63 → 0.1.64
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 +45 -29
- package/dist/bundle.cjs.js +3 -3
- package/dist/bundle.cjs.js.map +1 -1
- package/dist/bundle.es.js +509 -371
- package/dist/bundle.es.js.map +1 -1
- package/dist/bundle.iife.js +3 -3
- package/dist/bundle.iife.js.map +1 -1
- package/dist/bundle.umd.js +3 -3
- package/dist/bundle.umd.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/protocol/rest.d.ts +2 -0
- package/dist/protocol/rest.d.ts.map +1 -1
- package/dist/protocol/transaction.d.ts +7 -3
- package/dist/protocol/transaction.d.ts.map +1 -1
- package/dist/protocol/webrtc.d.ts +1 -0
- package/dist/protocol/webrtc.d.ts.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/tools.d.ts +72 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/playground/src/chat.tsx +12 -16
- package/playground/src/tools.ts +127 -0
- package/src/index.ts +1 -0
- package/src/protocol/rest.ts +8 -1
- package/src/protocol/transaction.ts +27 -17
- package/src/protocol/webrtc.ts +22 -3
- package/src/runtime.tsx +22 -12
- package/src/tools.ts +211 -0
- package/src/types.ts +6 -2
package/playground/src/chat.tsx
CHANGED
|
@@ -3,6 +3,8 @@ import { PersonaRuntimeProvider, PersonaConsoleLogger, Session, usePersonaRuntim
|
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
import { TooltipProvider } from './components/ui/tooltip';
|
|
6
|
+
import { tools } from '@/tools';
|
|
7
|
+
|
|
6
8
|
const logger = new PersonaConsoleLogger();
|
|
7
9
|
|
|
8
10
|
function generateSID() {
|
|
@@ -58,7 +60,8 @@ function MessageLogger() {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function Chat() {
|
|
61
|
-
const
|
|
63
|
+
const agentId = getQueryParam('agentId');
|
|
64
|
+
const apiKey = getQueryParam('apiKey');
|
|
62
65
|
const sid = getQueryParam('sid');
|
|
63
66
|
|
|
64
67
|
const session = useMemo((): Session => {
|
|
@@ -74,26 +77,19 @@ function Chat() {
|
|
|
74
77
|
logger={logger}
|
|
75
78
|
context={{
|
|
76
79
|
data: {
|
|
77
|
-
username: '
|
|
78
|
-
userAgent:
|
|
79
|
-
},
|
|
80
|
-
}}
|
|
81
|
-
tools={{
|
|
82
|
-
get_user_agent: () => {
|
|
83
|
-
const userAgent = 'Bruno Trimone Agent';
|
|
84
|
-
return { userAgent };
|
|
85
|
-
},
|
|
86
|
-
get_client_date: () => {
|
|
87
|
-
const date = new Date();
|
|
88
|
-
return { date: date.toISOString() };
|
|
80
|
+
username: 'Roberto',
|
|
81
|
+
userAgent: 'Google Tridroid 14',
|
|
89
82
|
},
|
|
90
83
|
}}
|
|
84
|
+
tools={tools}
|
|
91
85
|
protocols={{
|
|
92
|
-
rest:
|
|
86
|
+
rest: false,
|
|
87
|
+
websocket: true,
|
|
88
|
+
webrtc: true,
|
|
93
89
|
}}
|
|
94
90
|
session={session}
|
|
95
|
-
apiKey=
|
|
96
|
-
agentId={
|
|
91
|
+
apiKey={apiKey!}
|
|
92
|
+
agentId={agentId!}
|
|
97
93
|
>
|
|
98
94
|
<div className="h-dvh w-full">
|
|
99
95
|
<TooltipProvider>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example Tool Library
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how to define and export a library of example tools
|
|
5
|
+
* using the Persona SDK's createTool utility. These tools can be imported
|
|
6
|
+
* and used in PersonaRuntimeProvider or other parts of the application to
|
|
7
|
+
* showcase or test tool integration.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createTool, PersonaTools } from '@applica-software-guru/persona-sdk';
|
|
11
|
+
|
|
12
|
+
const toolLibrary = [
|
|
13
|
+
createTool({
|
|
14
|
+
name: 'sum',
|
|
15
|
+
title: 'Sum two numbers',
|
|
16
|
+
description: 'Sums two numbers and returns the result.',
|
|
17
|
+
parameters: {
|
|
18
|
+
a: { type: 'number', description: 'First number to sum', required: true },
|
|
19
|
+
b: { type: 'number', description: 'Second number to sum', required: true },
|
|
20
|
+
},
|
|
21
|
+
output: {
|
|
22
|
+
result: { type: 'number', description: 'Sum of the two numbers' },
|
|
23
|
+
},
|
|
24
|
+
implementation: async (a: number, b: number) => {
|
|
25
|
+
const result = a + b;
|
|
26
|
+
return { result };
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
createTool({
|
|
30
|
+
name: 'concat',
|
|
31
|
+
title: 'Concatenate two strings',
|
|
32
|
+
description: 'Concatenates two strings and returns the result.',
|
|
33
|
+
parameters: {
|
|
34
|
+
str1: { type: 'string', description: 'First string to concatenate', required: true },
|
|
35
|
+
str2: { type: 'string', description: 'Second string to concatenate', required: true },
|
|
36
|
+
},
|
|
37
|
+
output: {
|
|
38
|
+
result: { type: 'string', description: 'Concatenated result of the two strings' },
|
|
39
|
+
},
|
|
40
|
+
implementation: async (str1: string, str2: string) => {
|
|
41
|
+
const result = str1 + str2;
|
|
42
|
+
return { result };
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
// User agent tool:
|
|
46
|
+
createTool({
|
|
47
|
+
name: 'userAgent',
|
|
48
|
+
title: 'Get User Agent',
|
|
49
|
+
description: 'Retrieves the user agent string from the client.',
|
|
50
|
+
parameters: {},
|
|
51
|
+
output: {
|
|
52
|
+
result: { type: 'string', description: 'User agent string' },
|
|
53
|
+
},
|
|
54
|
+
implementation: async () => {
|
|
55
|
+
return { result: navigator.userAgent };
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
// Navigate to URL tool:
|
|
59
|
+
createTool({
|
|
60
|
+
name: 'navigateTo',
|
|
61
|
+
title: 'Navigate to URL',
|
|
62
|
+
description: 'Navigates the browser to a specified URL.',
|
|
63
|
+
parameters: {
|
|
64
|
+
url: { type: 'string', description: 'The URL to navigate to', required: true },
|
|
65
|
+
},
|
|
66
|
+
output: {
|
|
67
|
+
success: { type: 'boolean', description: 'Indicates if navigation was successful' },
|
|
68
|
+
},
|
|
69
|
+
implementation: async (url: string) => {
|
|
70
|
+
window.location.href = url;
|
|
71
|
+
return { success: true };
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
// Get current URL tool:
|
|
75
|
+
createTool({
|
|
76
|
+
name: 'getCurrentUrl',
|
|
77
|
+
title: 'Get Current URL',
|
|
78
|
+
description: 'Retrieves the current browser URL.',
|
|
79
|
+
parameters: {},
|
|
80
|
+
output: {
|
|
81
|
+
url: { type: 'string', description: 'Current browser URL' },
|
|
82
|
+
},
|
|
83
|
+
implementation: async () => {
|
|
84
|
+
return { url: window.location.href };
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
|
|
88
|
+
// Current client date and time tool:
|
|
89
|
+
createTool({
|
|
90
|
+
name: 'getCurrentDateTime',
|
|
91
|
+
title: 'Get Current Date and Time',
|
|
92
|
+
description: 'Retrieves the current date and time from the client.',
|
|
93
|
+
parameters: {},
|
|
94
|
+
output: {
|
|
95
|
+
dateTime: { type: 'string', description: 'Current date and time in ISO format' },
|
|
96
|
+
},
|
|
97
|
+
implementation: async () => {
|
|
98
|
+
return { dateTime: new Date().toISOString() };
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const tools: PersonaTools = {
|
|
104
|
+
sum: function (a: number, b: number) {
|
|
105
|
+
const result = a + b;
|
|
106
|
+
return { result };
|
|
107
|
+
},
|
|
108
|
+
concat: function (str1: string, str2: string) {
|
|
109
|
+
const result = str1 + str2;
|
|
110
|
+
return { result };
|
|
111
|
+
},
|
|
112
|
+
navigateTo: function (url: string) {
|
|
113
|
+
window.location.href = url;
|
|
114
|
+
return { success: true };
|
|
115
|
+
},
|
|
116
|
+
getUserAgent: function () {
|
|
117
|
+
return { result: navigator.userAgent };
|
|
118
|
+
},
|
|
119
|
+
getCurrentUrl: function () {
|
|
120
|
+
return { url: window.location.href };
|
|
121
|
+
},
|
|
122
|
+
getCurrentDateTime: function () {
|
|
123
|
+
return { dateTime: new Date().toISOString() };
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export { toolLibrary, tools };
|
package/src/index.ts
CHANGED
package/src/protocol/rest.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PersonaPacket,
|
|
9
9
|
PersonaCommand,
|
|
10
10
|
} from '../types';
|
|
11
|
+
import { ToolInstance } from '../tools';
|
|
11
12
|
|
|
12
13
|
type PersonaRESTProtocolConfig = PersonaProtocolBaseConfig & {
|
|
13
14
|
apiUrl: string;
|
|
@@ -20,6 +21,7 @@ class PersonaRESTProtocol extends PersonaProtocolBase {
|
|
|
20
21
|
config: PersonaRESTProtocolConfig;
|
|
21
22
|
notify: boolean = true;
|
|
22
23
|
context: Record<string, any> = {};
|
|
24
|
+
tools: ToolInstance[] = [];
|
|
23
25
|
|
|
24
26
|
constructor(config: PersonaRESTProtocolConfig) {
|
|
25
27
|
super();
|
|
@@ -56,10 +58,15 @@ class PersonaRESTProtocol extends PersonaProtocolBase {
|
|
|
56
58
|
if (packet.type === 'command' && (packet?.payload as PersonaCommand)?.command == 'set_initial_context') {
|
|
57
59
|
this.context = (packet?.payload as PersonaCommand)?.arguments;
|
|
58
60
|
return;
|
|
61
|
+
} else if (packet.type === 'command' && (packet?.payload as PersonaCommand)?.command == 'set_local_tools') {
|
|
62
|
+
const args = (packet?.payload as PersonaCommand)?.arguments;
|
|
63
|
+
this.tools = args?.tools as ToolInstance[];
|
|
64
|
+
this.config?.logger?.warn('Local tools are not supported in REST protocol, ignoring command');
|
|
65
|
+
return;
|
|
59
66
|
}
|
|
60
67
|
const input = packet.payload as PersonaMessage;
|
|
61
68
|
const response = await fetch(`${apiUrl}/sessions/${sessionId}/messages`, {
|
|
62
|
-
body: JSON.stringify({ agentId, userMessage: input,
|
|
69
|
+
body: JSON.stringify({ agentId, userMessage: input, initialContext: this.context, tools: this.tools }),
|
|
63
70
|
method: 'POST',
|
|
64
71
|
headers: {
|
|
65
72
|
'Content-Type': 'application/json',
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { PersonaProtocolBase } from './base';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
ProtocolStatus,
|
|
5
|
-
PersonaProtocolBaseConfig,
|
|
6
|
-
PersonaTransaction,
|
|
7
|
-
FunctionCall,
|
|
8
|
-
ReadonlyJSONObject,
|
|
9
|
-
PersonaPacket,
|
|
10
|
-
} from '../types';
|
|
2
|
+
import { Session, ProtocolStatus, PersonaProtocolBaseConfig, PersonaTransaction, FunctionCall, PersonaPacket } from '../types';
|
|
3
|
+
import { ToolInstance } from '../tools';
|
|
11
4
|
|
|
12
5
|
type FinishTransactionRequest = {
|
|
13
6
|
success: boolean;
|
|
@@ -45,7 +38,7 @@ class PersonaTransactionsManager {
|
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
export type PersonaToolCallback = (args:
|
|
41
|
+
export type PersonaToolCallback = (...args: any[]) => void | Record<string, any> | Promise<void | Record<string, any>>;
|
|
49
42
|
export type PersonaTools = {
|
|
50
43
|
[key: string]: PersonaToolCallback;
|
|
51
44
|
};
|
|
@@ -77,7 +70,7 @@ class PersonaPersistableTransaction {
|
|
|
77
70
|
return;
|
|
78
71
|
}
|
|
79
72
|
try {
|
|
80
|
-
const result = await tool(functionArgs);
|
|
73
|
+
const result = await tool.apply(null, Object.values(functionArgs));
|
|
81
74
|
await this.complete(result);
|
|
82
75
|
} catch (error) {
|
|
83
76
|
await this.fail(`Error executing tool ${functionName}: ${error}`);
|
|
@@ -95,7 +88,8 @@ type PersonaTransactionCallback = (transaction: PersonaPersistableTransaction) =
|
|
|
95
88
|
|
|
96
89
|
type PersonaTransactionProtocolConfig = PersonaProtocolBaseConfig & {
|
|
97
90
|
apiUrl: string;
|
|
98
|
-
|
|
91
|
+
tools: PersonaTools | ToolInstance[];
|
|
92
|
+
onTransaction?: PersonaTransactionCallback;
|
|
99
93
|
};
|
|
100
94
|
|
|
101
95
|
class PersonaTransactionProtocol extends PersonaProtocolBase {
|
|
@@ -104,12 +98,23 @@ class PersonaTransactionProtocol extends PersonaProtocolBase {
|
|
|
104
98
|
session: Session;
|
|
105
99
|
config: PersonaTransactionProtocolConfig;
|
|
106
100
|
notify: boolean = true;
|
|
101
|
+
private _tools: PersonaTools;
|
|
107
102
|
|
|
108
103
|
constructor(config: PersonaTransactionProtocolConfig) {
|
|
109
104
|
super();
|
|
110
105
|
this.config = config;
|
|
111
106
|
this.status = 'disconnected';
|
|
112
107
|
this.autostart = true;
|
|
108
|
+
if (Array.isArray(config.tools)) {
|
|
109
|
+
this._tools = {};
|
|
110
|
+
for (const tool of config.tools as ToolInstance[]) {
|
|
111
|
+
if (tool.schema && tool.implementation) {
|
|
112
|
+
this._tools[tool.schema.name] = tool.implementation;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
this._tools = config.tools as PersonaTools;
|
|
117
|
+
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
public getName(): string {
|
|
@@ -137,13 +142,18 @@ class PersonaTransactionProtocol extends PersonaProtocolBase {
|
|
|
137
142
|
public async sendPacket(_: PersonaPacket): Promise<void> {}
|
|
138
143
|
|
|
139
144
|
public onTransaction(transaction: PersonaTransaction): void {
|
|
140
|
-
|
|
141
|
-
this.config.logger?.error('Transaction protocol config is not set');
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
145
|
+
console.log('transaction received:', transaction);
|
|
144
146
|
const manager = new PersonaTransactionsManager(this.config);
|
|
145
147
|
const persistable = new PersonaPersistableTransaction(transaction, manager);
|
|
146
|
-
this.config.onTransaction
|
|
148
|
+
if (this.config.onTransaction) {
|
|
149
|
+
this.config.onTransaction(persistable);
|
|
150
|
+
} else {
|
|
151
|
+
persistable.invoke(this._tools);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public getTools(): PersonaTools {
|
|
156
|
+
return this._tools;
|
|
147
157
|
}
|
|
148
158
|
}
|
|
149
159
|
|
package/src/protocol/webrtc.ts
CHANGED
|
@@ -31,6 +31,7 @@ class PersonaWebRTCClient {
|
|
|
31
31
|
private isConnected: boolean = false;
|
|
32
32
|
private visualizerCallbacks: AudioVisualizerCallback[] = [];
|
|
33
33
|
private messageCallbacks: PersonaWebRTCMessageCallback[] = [];
|
|
34
|
+
private queuedMessages: PersonaPacket[] = [];
|
|
34
35
|
|
|
35
36
|
constructor(config: PersonaWebRTCConfig) {
|
|
36
37
|
this.config = config;
|
|
@@ -100,6 +101,15 @@ class PersonaWebRTCClient {
|
|
|
100
101
|
callback(msg);
|
|
101
102
|
});
|
|
102
103
|
};
|
|
104
|
+
channel.onopen = () => {
|
|
105
|
+
while (this.queuedMessages.length > 0) {
|
|
106
|
+
const packet = this.queuedMessages.shift();
|
|
107
|
+
if (packet) {
|
|
108
|
+
channel.send(JSON.stringify(packet));
|
|
109
|
+
this.config.logger?.info('Sent queued message:', packet);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
103
113
|
};
|
|
104
114
|
|
|
105
115
|
const url = this.config.webrtcUrl || 'wss://persona.applica.guru/api/webrtc';
|
|
@@ -179,7 +189,16 @@ class PersonaWebRTCClient {
|
|
|
179
189
|
public createDataChannel(label = 'messages'): void {
|
|
180
190
|
if (!this.pc) return;
|
|
181
191
|
this.dataChannel = this.pc.createDataChannel(label);
|
|
182
|
-
this.dataChannel.onopen = () =>
|
|
192
|
+
this.dataChannel.onopen = () => {
|
|
193
|
+
this.config.logger?.info('Data channel opened');
|
|
194
|
+
while (this.queuedMessages.length > 0) {
|
|
195
|
+
const packet = this.queuedMessages.shift();
|
|
196
|
+
if (packet) {
|
|
197
|
+
this.dataChannel!.send(JSON.stringify(packet));
|
|
198
|
+
this.config.logger?.info('Sent queued message:', packet);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
183
202
|
this.dataChannel.onmessage = (msg: MessageEvent) => {
|
|
184
203
|
this.messageCallbacks.forEach((callback) => {
|
|
185
204
|
callback(msg);
|
|
@@ -188,8 +207,8 @@ class PersonaWebRTCClient {
|
|
|
188
207
|
}
|
|
189
208
|
|
|
190
209
|
public sendPacket(packet: PersonaPacket): void {
|
|
191
|
-
if (!this.dataChannel) {
|
|
192
|
-
this.
|
|
210
|
+
if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
|
|
211
|
+
this.queuedMessages.push(packet);
|
|
193
212
|
return;
|
|
194
213
|
}
|
|
195
214
|
|
package/src/runtime.tsx
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
} from './types';
|
|
21
21
|
import { parseMessages, convertMessage } from './messages';
|
|
22
22
|
import {
|
|
23
|
-
PersonaPersistableTransaction,
|
|
24
23
|
PersonaRESTProtocol,
|
|
25
24
|
PersonaRESTProtocolConfig,
|
|
26
25
|
PersonaTransactionProtocol,
|
|
@@ -137,9 +136,7 @@ function PersonaRuntimeProviderInner({
|
|
|
137
136
|
apiUrl: `${baseEndpointProtocol}://${baseEndpoint}`,
|
|
138
137
|
apiKey: config.apiKey,
|
|
139
138
|
agentId: config.agentId,
|
|
140
|
-
|
|
141
|
-
await transaction.invoke(config.tools!);
|
|
142
|
-
},
|
|
139
|
+
tools: config.tools, // Pass raw tools
|
|
143
140
|
logger,
|
|
144
141
|
}),
|
|
145
142
|
);
|
|
@@ -163,14 +160,27 @@ function PersonaRuntimeProviderInner({
|
|
|
163
160
|
protocol.addStatusChangeListener((status: ProtocolStatus) => {
|
|
164
161
|
logger?.debug(`${protocol.getName()} has notified new status: ${status}`);
|
|
165
162
|
protocolsStatus.set(protocol.getName(), status);
|
|
166
|
-
if (status === 'connected'
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
if (status === 'connected') {
|
|
164
|
+
if (config.context) {
|
|
165
|
+
protocol.sendPacket({
|
|
166
|
+
type: 'command',
|
|
167
|
+
payload: {
|
|
168
|
+
command: 'set_initial_context',
|
|
169
|
+
arguments: config.context,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (config.tools && Array.isArray(config.tools)) {
|
|
174
|
+
protocol.sendPacket({
|
|
175
|
+
type: 'command',
|
|
176
|
+
payload: {
|
|
177
|
+
command: 'set_local_tools',
|
|
178
|
+
arguments: {
|
|
179
|
+
tools: config.tools.map((tool) => tool.schema),
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
174
184
|
}
|
|
175
185
|
setProtocolsStatus(new Map(protocolsStatus));
|
|
176
186
|
});
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export type ToolParameterType = 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
2
|
+
|
|
3
|
+
export interface ToolParameter {
|
|
4
|
+
type: ToolParameterType;
|
|
5
|
+
description: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
properties?: Record<string, ToolParameter>;
|
|
8
|
+
items?: ToolParameter;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ToolSchema {
|
|
12
|
+
type: 'local';
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
config: {
|
|
16
|
+
timeout: number;
|
|
17
|
+
parameters: {
|
|
18
|
+
type: 'object';
|
|
19
|
+
title: string;
|
|
20
|
+
required: string[];
|
|
21
|
+
properties: Record<string, ToolParameter>;
|
|
22
|
+
};
|
|
23
|
+
output: {
|
|
24
|
+
type: 'object';
|
|
25
|
+
title: string;
|
|
26
|
+
properties: Record<string, ToolParameter>;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ToolDefinition {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
parameters: Record<string, ToolParameter>;
|
|
37
|
+
output: Record<string, ToolParameter>;
|
|
38
|
+
implementation: (...args: any[]) => any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a tool parameter definition
|
|
43
|
+
*/
|
|
44
|
+
export function createParameter(
|
|
45
|
+
type: ToolParameterType,
|
|
46
|
+
description: string,
|
|
47
|
+
options?: {
|
|
48
|
+
required?: boolean;
|
|
49
|
+
properties?: Record<string, ToolParameter>;
|
|
50
|
+
items?: ToolParameter;
|
|
51
|
+
},
|
|
52
|
+
): ToolParameter {
|
|
53
|
+
return {
|
|
54
|
+
type,
|
|
55
|
+
description,
|
|
56
|
+
...options,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generate a tool schema from a tool definition
|
|
62
|
+
*/
|
|
63
|
+
export function generateToolSchema(definition: ToolDefinition): ToolSchema {
|
|
64
|
+
const requiredParams = Object.entries(definition.parameters)
|
|
65
|
+
.filter(([_, param]) => param.required)
|
|
66
|
+
.map(([name]) => name);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
type: 'local',
|
|
70
|
+
name: definition.name,
|
|
71
|
+
description: definition.description,
|
|
72
|
+
config: {
|
|
73
|
+
timeout: definition.timeout || 60,
|
|
74
|
+
parameters: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
title: definition.title || `${definition.name} parameters`,
|
|
77
|
+
required: requiredParams,
|
|
78
|
+
properties: definition.parameters,
|
|
79
|
+
},
|
|
80
|
+
output: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
title: `${definition.name} output`,
|
|
83
|
+
properties: definition.output,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type ToolInstance = { schema: ToolSchema; implementation: (...args: any[]) => any };
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create a complete tool definition with schema and implementation
|
|
93
|
+
*/
|
|
94
|
+
export function createTool(definition: ToolDefinition): ToolInstance {
|
|
95
|
+
return {
|
|
96
|
+
schema: generateToolSchema(definition),
|
|
97
|
+
implementation: definition.implementation,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Extract function signature and generate schema from a JavaScript function
|
|
103
|
+
* This is a utility to help convert existing functions to tool schemas
|
|
104
|
+
*/
|
|
105
|
+
export function createToolFromFunction(
|
|
106
|
+
name: string,
|
|
107
|
+
description: string,
|
|
108
|
+
fn: (...args: any[]) => any,
|
|
109
|
+
parameterTypes: Record<string, ToolParameter>,
|
|
110
|
+
outputTypes: Record<string, ToolParameter>,
|
|
111
|
+
options?: {
|
|
112
|
+
title?: string;
|
|
113
|
+
timeout?: number;
|
|
114
|
+
},
|
|
115
|
+
): ToolInstance {
|
|
116
|
+
const definition: ToolDefinition = {
|
|
117
|
+
name,
|
|
118
|
+
description,
|
|
119
|
+
title: options?.title,
|
|
120
|
+
timeout: options?.timeout,
|
|
121
|
+
parameters: parameterTypes,
|
|
122
|
+
output: outputTypes,
|
|
123
|
+
implementation: fn,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return createTool(definition);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Example usage for the sum function you provided:
|
|
130
|
+
export const sumTool = createToolFromFunction(
|
|
131
|
+
'sum',
|
|
132
|
+
'Sum two numbers',
|
|
133
|
+
function sum(a: number, b: number) {
|
|
134
|
+
const result = a + b;
|
|
135
|
+
return { result };
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
a: createParameter('number', 'First number to sum', { required: true }),
|
|
139
|
+
b: createParameter('number', 'Second number to sum', { required: true }),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
result: createParameter('number', 'Sum of two numbers'),
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Example for the navigate_to function as shown in the user's request:
|
|
147
|
+
export const navigateToToolExample = createTool({
|
|
148
|
+
name: 'navigate_to',
|
|
149
|
+
description: 'Allow agent to redirect user to specific sub page like /foo or #/foo or anything like that',
|
|
150
|
+
title: 'Sum two numbers', // As per the user's example
|
|
151
|
+
timeout: 60,
|
|
152
|
+
parameters: {
|
|
153
|
+
a: createParameter('number', 'First number to sum'),
|
|
154
|
+
b: createParameter('number', 'Seconth number to sum'), // Keeping the typo as in the original
|
|
155
|
+
},
|
|
156
|
+
output: {
|
|
157
|
+
result: createParameter('number', 'Sum of two numbers'),
|
|
158
|
+
},
|
|
159
|
+
implementation: function navigateTo(a: number, b: number) {
|
|
160
|
+
// This is just an example - you would implement actual navigation logic here
|
|
161
|
+
const result = a + b;
|
|
162
|
+
return { result };
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Helper function to create multiple tools at once
|
|
167
|
+
export function createToolRegistry(tools: ToolDefinition[]): {
|
|
168
|
+
schemas: ToolSchema[];
|
|
169
|
+
implementations: Record<string, (...args: any[]) => any>;
|
|
170
|
+
} {
|
|
171
|
+
const schemas: ToolSchema[] = [];
|
|
172
|
+
const implementations: Record<string, (...args: any[]) => any> = {};
|
|
173
|
+
|
|
174
|
+
tools.forEach((tool) => {
|
|
175
|
+
const { schema, implementation } = createTool(tool);
|
|
176
|
+
schemas.push(schema);
|
|
177
|
+
implementations[tool.name] = implementation;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return { schemas, implementations };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Utility to validate tool parameters at runtime
|
|
184
|
+
export function validateToolParameters(parameters: Record<string, any>, schema: ToolSchema): boolean {
|
|
185
|
+
const { required, properties } = schema.config.parameters;
|
|
186
|
+
|
|
187
|
+
// Check required parameters
|
|
188
|
+
for (const requiredParam of required) {
|
|
189
|
+
if (!(requiredParam in parameters)) {
|
|
190
|
+
throw new Error(`Missing required parameter: ${requiredParam}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Type checking (basic)
|
|
195
|
+
for (const [paramName, paramValue] of Object.entries(parameters)) {
|
|
196
|
+
const paramSchema = properties[paramName];
|
|
197
|
+
if (paramSchema) {
|
|
198
|
+
if (paramSchema.type === 'number' && typeof paramValue !== 'number') {
|
|
199
|
+
throw new Error(`Parameter ${paramName} should be a number`);
|
|
200
|
+
}
|
|
201
|
+
if (paramSchema.type === 'string' && typeof paramValue !== 'string') {
|
|
202
|
+
throw new Error(`Parameter ${paramName} should be a string`);
|
|
203
|
+
}
|
|
204
|
+
if (paramSchema.type === 'boolean' && typeof paramValue !== 'boolean') {
|
|
205
|
+
throw new Error(`Parameter ${paramName} should be a boolean`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return true;
|
|
211
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PersonaTools } from 'src/protocol';
|
|
2
2
|
import { PersonaLogger } from './logging';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
+
import type { ToolInstance } from './tools';
|
|
4
5
|
|
|
5
6
|
export type ReadonlyJSONObject = {
|
|
6
7
|
readonly [key: string]: string | number | boolean | null | ReadonlyJSONObject | ReadonlyArray<ReadonlyJSONObject>;
|
|
@@ -79,7 +80,7 @@ export type PersonaSource = {
|
|
|
79
80
|
};
|
|
80
81
|
|
|
81
82
|
export type PersonaCommand = {
|
|
82
|
-
command: 'get_mcp_assets' | 'set_initial_context';
|
|
83
|
+
command: 'get_mcp_assets' | 'set_initial_context' | 'set_local_tools';
|
|
83
84
|
arguments: Record<string, unknown>;
|
|
84
85
|
};
|
|
85
86
|
|
|
@@ -182,5 +183,8 @@ export type PersonaConfig = PersonaBaseConfig &
|
|
|
182
183
|
websocket?: PersonaProtocolBaseConfig | boolean;
|
|
183
184
|
};
|
|
184
185
|
|
|
185
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Tools can be provided as an object (legacy) or as an array of { schema, implementation } (recommended)
|
|
188
|
+
*/
|
|
189
|
+
tools?: PersonaTools | ToolInstance[];
|
|
186
190
|
};
|