@applica-software-guru/persona-sdk 0.1.45 → 0.1.47
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 +217 -0
- package/dist/bundle.cjs.js +2 -2
- package/dist/bundle.cjs.js.map +1 -1
- package/dist/bundle.es.js +332 -198
- package/dist/bundle.es.js.map +1 -1
- package/dist/bundle.iife.js +2 -2
- package/dist/bundle.iife.js.map +1 -1
- package/dist/bundle.umd.js +2 -2
- package/dist/bundle.umd.js.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/protocol/base.d.ts +4 -3
- package/dist/protocol/base.d.ts.map +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/rest.d.ts.map +1 -1
- package/dist/protocol/transaction.d.ts +50 -0
- package/dist/protocol/transaction.d.ts.map +1 -0
- package/dist/protocol/webrtc.d.ts.map +1 -1
- package/dist/protocol/websocket.d.ts.map +1 -1
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/types.d.ts +42 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/playground/src/chat.tsx +10 -1
- package/playground/src/components/assistant-ui/thread.tsx +58 -3
- package/src/messages.ts +20 -3
- package/src/protocol/base.ts +14 -3
- package/src/protocol/index.ts +1 -0
- package/src/protocol/rest.ts +6 -1
- package/src/protocol/transaction.ts +154 -0
- package/src/protocol/webrtc.ts +3 -6
- package/src/protocol/websocket.ts +3 -9
- package/src/runtime.tsx +42 -4
- package/src/types.ts +49 -2
|
@@ -3,12 +3,25 @@ import {
|
|
|
3
3
|
ActionBarPrimitive,
|
|
4
4
|
BranchPickerPrimitive,
|
|
5
5
|
ComposerPrimitive,
|
|
6
|
+
FileContentPart,
|
|
7
|
+
ImageContentPart,
|
|
6
8
|
MessagePrimitive,
|
|
7
9
|
ReasoningContentPartProps,
|
|
8
10
|
ThreadPrimitive,
|
|
9
11
|
} from '@assistant-ui/react';
|
|
10
|
-
import { useCallback, useMemo, type FC } from 'react';
|
|
11
|
-
import {
|
|
12
|
+
import { JSX, useCallback, useMemo, type FC } from 'react';
|
|
13
|
+
import {
|
|
14
|
+
ArrowDownIcon,
|
|
15
|
+
CheckIcon,
|
|
16
|
+
ChevronLeftIcon,
|
|
17
|
+
ChevronRightIcon,
|
|
18
|
+
CopyIcon,
|
|
19
|
+
SendHorizontalIcon,
|
|
20
|
+
MicIcon,
|
|
21
|
+
FileIcon,
|
|
22
|
+
ImageIcon,
|
|
23
|
+
ArchiveIcon,
|
|
24
|
+
} from 'lucide-react';
|
|
12
25
|
import { cn } from '@/lib/utils';
|
|
13
26
|
|
|
14
27
|
import { Button } from '@/components/ui/button';
|
|
@@ -16,7 +29,7 @@ import { MarkdownText } from '@/components/assistant-ui/markdown-text';
|
|
|
16
29
|
import { TooltipIconButton } from '@/components/assistant-ui/tooltip-icon-button';
|
|
17
30
|
import { ToolFallback } from '@/components/assistant-ui/tool-fallback';
|
|
18
31
|
|
|
19
|
-
import { usePersonaRuntimeWebRTCProtocol } from '@applica-software-guru/persona-sdk';
|
|
32
|
+
import { usePersonaRuntimeEndpoint, usePersonaRuntimeWebRTCProtocol } from '@applica-software-guru/persona-sdk';
|
|
20
33
|
|
|
21
34
|
export const Thread: FC = () => {
|
|
22
35
|
return (
|
|
@@ -172,6 +185,47 @@ const Reasoning: FC<ReasoningContentPartProps> = ({ text }: ReasoningContentPart
|
|
|
172
185
|
return <div className="text-sm text-muted-foreground break-words whitespace-pre-wrap">{text}</div>;
|
|
173
186
|
};
|
|
174
187
|
|
|
188
|
+
const Image: FC<ImageContentPart> = ({ image }: ImageContentPart) => {
|
|
189
|
+
const baseEndpoint = usePersonaRuntimeEndpoint();
|
|
190
|
+
return (
|
|
191
|
+
<div className="flex items-center gap-2 border rounded-lg border-muted bg-muted p-4 my-2">
|
|
192
|
+
<img src={`${baseEndpoint}/files/${image}`} alt="Image" className="max-h-96 max-w-full rounded-lg object-cover" />
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const File: FC<FileContentPart> = ({ data, mimeType }) => {
|
|
198
|
+
const isImage = useMemo(() => mimeType.startsWith('image/'), [mimeType]);
|
|
199
|
+
if (isImage) {
|
|
200
|
+
return <Image image={data} type="image" />;
|
|
201
|
+
}
|
|
202
|
+
const fileName = useMemo(() => {
|
|
203
|
+
const parts = data.split('/');
|
|
204
|
+
return parts[parts.length - 1];
|
|
205
|
+
}, [data]);
|
|
206
|
+
const baseEndpoint = usePersonaRuntimeEndpoint();
|
|
207
|
+
const getIconByMimeType = (mimeType: string) => {
|
|
208
|
+
const iconMap: Record<string, JSX.Element> = {
|
|
209
|
+
'application/pdf': <FileIcon />,
|
|
210
|
+
'image/png': <ImageIcon />,
|
|
211
|
+
'image/jpeg': <ImageIcon />,
|
|
212
|
+
'application/zip': <ArchiveIcon />,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return iconMap[mimeType] || <FileIcon />;
|
|
216
|
+
};
|
|
217
|
+
const icon = getIconByMimeType(mimeType);
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<div className="flex items-center gap-2 border rounded-lg border-muted bg-muted p-4 my-2">
|
|
221
|
+
<div className="text-muted-foreground">{icon}</div>
|
|
222
|
+
<a target="_blank" href={`${baseEndpoint}/files/${data}`} download={fileName} className="text-md text-muted-foreground">
|
|
223
|
+
{fileName}
|
|
224
|
+
</a>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
|
|
175
229
|
const AssistantMessage: FC = () => {
|
|
176
230
|
return (
|
|
177
231
|
<MessagePrimitive.Root className="grid grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] relative w-full max-w-[var(--thread-max-width)] py-4">
|
|
@@ -179,6 +233,7 @@ const AssistantMessage: FC = () => {
|
|
|
179
233
|
<MessagePrimitive.Content
|
|
180
234
|
components={{
|
|
181
235
|
Text: MarkdownText,
|
|
236
|
+
File,
|
|
182
237
|
Reasoning,
|
|
183
238
|
tools: {
|
|
184
239
|
Fallback: ToolFallback,
|
package/src/messages.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PersonaMessage } from './types';
|
|
2
|
-
import { ThreadMessageLike } from '@assistant-ui/react';
|
|
2
|
+
import { FileContentPart, ThreadMessageLike } from '@assistant-ui/react';
|
|
3
3
|
|
|
4
4
|
function removeEmptyMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
5
5
|
return messages.filter((message) => {
|
|
@@ -14,6 +14,9 @@ function parseMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
|
14
14
|
let currentMessage: PersonaMessage | null = null;
|
|
15
15
|
|
|
16
16
|
for (const message of messages) {
|
|
17
|
+
if (message.type === 'transaction') {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
17
20
|
if (message.type === 'reasoning') {
|
|
18
21
|
if (currentMessage != null) {
|
|
19
22
|
outputMessages.push(currentMessage);
|
|
@@ -37,6 +40,7 @@ function parseMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
|
37
40
|
(currentMessage.role === message.role || message.finishReason === 'stop')
|
|
38
41
|
) {
|
|
39
42
|
currentMessage.text += message.text;
|
|
43
|
+
currentMessage.files = [...(currentMessage.files ?? []), ...(message.files ?? [])];
|
|
40
44
|
} else {
|
|
41
45
|
if (currentMessage) {
|
|
42
46
|
outputMessages.push(currentMessage);
|
|
@@ -50,10 +54,20 @@ function parseMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
|
50
54
|
if (currentMessage) {
|
|
51
55
|
outputMessages.push(currentMessage);
|
|
52
56
|
}
|
|
53
|
-
|
|
57
|
+
const cleanMessages = removeEmptyMessages(outputMessages);
|
|
58
|
+
return cleanMessages;
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
function convertMessage(message: PersonaMessage): ThreadMessageLike {
|
|
62
|
+
const files =
|
|
63
|
+
message.files?.map(
|
|
64
|
+
(file) =>
|
|
65
|
+
({
|
|
66
|
+
type: 'file',
|
|
67
|
+
data: file.url,
|
|
68
|
+
mimeType: file.contentType,
|
|
69
|
+
} as FileContentPart),
|
|
70
|
+
) ?? [];
|
|
57
71
|
if (message.role === 'function') {
|
|
58
72
|
return {
|
|
59
73
|
id: message.id!,
|
|
@@ -72,7 +86,10 @@ function convertMessage(message: PersonaMessage): ThreadMessageLike {
|
|
|
72
86
|
return {
|
|
73
87
|
id: message.id!,
|
|
74
88
|
role: message.role,
|
|
75
|
-
content:
|
|
89
|
+
content:
|
|
90
|
+
message.type === 'reasoning'
|
|
91
|
+
? [{ type: 'reasoning', text: message.text }, ...files]
|
|
92
|
+
: [{ type: 'text', text: message.text }, ...files],
|
|
76
93
|
};
|
|
77
94
|
}
|
|
78
95
|
|
package/src/protocol/base.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Message,
|
|
3
|
+
MessageListenerCallback,
|
|
4
|
+
PersonaPayload,
|
|
5
|
+
PersonaProtocol,
|
|
6
|
+
PersonaTransaction,
|
|
7
|
+
ProtocolStatus,
|
|
8
|
+
Session,
|
|
9
|
+
StatusChangeCallback,
|
|
10
|
+
} from '../types';
|
|
2
11
|
|
|
3
12
|
abstract class PersonaProtocolBase implements PersonaProtocol {
|
|
4
13
|
abstract status: ProtocolStatus;
|
|
@@ -19,10 +28,10 @@ abstract class PersonaProtocolBase implements PersonaProtocol {
|
|
|
19
28
|
this.session = session;
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
public async notifyMessage(message:
|
|
31
|
+
public async notifyMessage(message: PersonaPayload): Promise<void> {
|
|
23
32
|
this.messageCallbacks.forEach((callback) => callback(message));
|
|
24
33
|
}
|
|
25
|
-
public async notifyMessages(messages:
|
|
34
|
+
public async notifyMessages(messages: PersonaPayload[]): Promise<void> {
|
|
26
35
|
messages.forEach((message) => {
|
|
27
36
|
this.messageCallbacks.forEach((callback) => callback(message));
|
|
28
37
|
});
|
|
@@ -50,6 +59,8 @@ abstract class PersonaProtocolBase implements PersonaProtocol {
|
|
|
50
59
|
abstract connect(session?: Session): Promise<Session>;
|
|
51
60
|
abstract disconnect(): Promise<void>;
|
|
52
61
|
abstract send(message: Message): Promise<void>;
|
|
62
|
+
|
|
63
|
+
public onTransaction(_: PersonaTransaction) {}
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
export { PersonaProtocolBase };
|
package/src/protocol/index.ts
CHANGED
package/src/protocol/rest.ts
CHANGED
|
@@ -56,7 +56,12 @@ class PersonaRESTProtocol extends PersonaProtocolBase {
|
|
|
56
56
|
},
|
|
57
57
|
});
|
|
58
58
|
const personaResponse = (await response.json()) as PersonaResponse;
|
|
59
|
-
this.notifyMessages(
|
|
59
|
+
this.notifyMessages(
|
|
60
|
+
personaResponse.response.messages.map((payload) => ({
|
|
61
|
+
type: 'message',
|
|
62
|
+
payload,
|
|
63
|
+
})),
|
|
64
|
+
);
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { PersonaProtocolBase } from './base';
|
|
2
|
+
import {
|
|
3
|
+
Message,
|
|
4
|
+
Session,
|
|
5
|
+
ProtocolStatus,
|
|
6
|
+
PersonaProtocolBaseConfig,
|
|
7
|
+
PersonaTransaction,
|
|
8
|
+
FunctionCall,
|
|
9
|
+
ReadonlyJSONObject,
|
|
10
|
+
} from '../types';
|
|
11
|
+
|
|
12
|
+
type FinishTransactionRequest = {
|
|
13
|
+
success: boolean;
|
|
14
|
+
output: any;
|
|
15
|
+
error: string | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
class PersonaTransactionsManager {
|
|
19
|
+
private config: PersonaTransactionProtocolConfig;
|
|
20
|
+
|
|
21
|
+
constructor(config: PersonaTransactionProtocolConfig) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async complete(transaction: PersonaTransaction, request: FinishTransactionRequest): Promise<void> {
|
|
26
|
+
await this.persist(transaction, { ...request, success: true });
|
|
27
|
+
this.config.logger?.debug('Transaction completed:', transaction);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async fail(transaction: PersonaTransaction, request: FinishTransactionRequest): Promise<void> {
|
|
31
|
+
await this.persist(transaction, { ...request, success: false });
|
|
32
|
+
this.config.logger?.debug('Transaction failed:', { ...transaction, ...request });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async persist(transaction: PersonaTransaction, request: FinishTransactionRequest): Promise<void> {
|
|
36
|
+
await fetch(`${this.config.apiUrl}/transactions/${transaction.id}`, {
|
|
37
|
+
body: JSON.stringify(request),
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
Accept: 'application/json',
|
|
42
|
+
'x-persona-apikey': this.config.apiKey,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type PersonaToolCallback = (args: ReadonlyJSONObject | undefined) => void;
|
|
49
|
+
export type PersonaTools = {
|
|
50
|
+
[key: string]: PersonaToolCallback;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
class PersonaPersistableTransaction {
|
|
54
|
+
private transaction: PersonaTransaction;
|
|
55
|
+
private manager: PersonaTransactionsManager;
|
|
56
|
+
|
|
57
|
+
constructor(transaction: PersonaTransaction, manager: PersonaTransactionsManager) {
|
|
58
|
+
this.transaction = transaction;
|
|
59
|
+
this.manager = manager;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public getFunctionCall(): FunctionCall | null {
|
|
63
|
+
return this.transaction.functionCall;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async invoke(tools: PersonaTools): Promise<void> {
|
|
67
|
+
const functionCall = this.transaction.functionCall;
|
|
68
|
+
if (!functionCall) {
|
|
69
|
+
await this.fail('No function call found');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const functionName = functionCall.name;
|
|
73
|
+
const functionArgs = functionCall.args;
|
|
74
|
+
const tool = tools[functionName];
|
|
75
|
+
if (!tool) {
|
|
76
|
+
await this.fail(`Tool ${functionName} not found`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const result = await tool(functionArgs);
|
|
81
|
+
await this.complete({ date: new Date().toISOString(), result });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
await this.fail(`Error executing tool ${functionName}: ${error}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async complete(output: any): Promise<void> {
|
|
87
|
+
await this.manager.complete(this.transaction, { success: true, output, error: null });
|
|
88
|
+
}
|
|
89
|
+
async fail(error: string): Promise<void> {
|
|
90
|
+
await this.manager.fail(this.transaction, { success: false, output: null, error });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type PersonaTransactionCallback = (transaction: PersonaPersistableTransaction) => void;
|
|
95
|
+
|
|
96
|
+
type PersonaTransactionProtocolConfig = PersonaProtocolBaseConfig & {
|
|
97
|
+
apiUrl: string;
|
|
98
|
+
onTransaction: PersonaTransactionCallback;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
class PersonaTransactionProtocol extends PersonaProtocolBase {
|
|
102
|
+
status: ProtocolStatus;
|
|
103
|
+
autostart: boolean;
|
|
104
|
+
session: Session;
|
|
105
|
+
config: PersonaTransactionProtocolConfig;
|
|
106
|
+
notify: boolean = true;
|
|
107
|
+
|
|
108
|
+
constructor(config: PersonaTransactionProtocolConfig) {
|
|
109
|
+
super();
|
|
110
|
+
this.config = config;
|
|
111
|
+
this.status = 'disconnected';
|
|
112
|
+
this.autostart = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public getName(): string {
|
|
116
|
+
return 'transaction';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public getPriority(): number {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public async connect(session: Session): Promise<Session> {
|
|
124
|
+
this.setStatus('connected');
|
|
125
|
+
return session;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async disconnect(): Promise<void> {
|
|
129
|
+
this.setStatus('disconnected');
|
|
130
|
+
this.session = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public async syncSession(session: Session): Promise<void> {
|
|
134
|
+
this.session = session;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async send(message: Message): Promise<void> {
|
|
138
|
+
this.config.logger?.debug('Sending message:', message);
|
|
139
|
+
throw new Error('Not implemented');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public onTransaction(transaction: PersonaTransaction): void {
|
|
143
|
+
if (!this.config.onTransaction) {
|
|
144
|
+
this.config.logger?.error('Transaction protocol config is not set');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const manager = new PersonaTransactionsManager(this.config);
|
|
148
|
+
const persistable = new PersonaPersistableTransaction(transaction, manager);
|
|
149
|
+
this.config.onTransaction(persistable);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { PersonaTransactionProtocol };
|
|
154
|
+
export type { PersonaTransactionProtocolConfig, FinishTransactionRequest, PersonaPersistableTransaction, PersonaTransactionCallback };
|
package/src/protocol/webrtc.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PersonaProtocolBase } from './base';
|
|
2
|
-
import { Message,
|
|
2
|
+
import { Message, PersonaPayload, PersonaProtocolBaseConfig, ProtocolStatus, Session } from '../types';
|
|
3
3
|
|
|
4
4
|
type AudioAnalysisData = {
|
|
5
5
|
localAmplitude: number;
|
|
@@ -274,11 +274,8 @@ class PersonaWebRTCProtocol extends PersonaProtocolBase {
|
|
|
274
274
|
this.autostart = config?.autostart ?? false;
|
|
275
275
|
this.webRTCClient = new PersonaWebRTCClient(config);
|
|
276
276
|
this.webRTCClient.addMessageCallback((msg: MessageEvent) => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (data.type === 'message') {
|
|
280
|
-
this.notifyMessage(data.payload as PersonaMessage);
|
|
281
|
-
}
|
|
277
|
+
const data = JSON.parse(msg.data) as PersonaPayload;
|
|
278
|
+
this.notifyMessage(data);
|
|
282
279
|
});
|
|
283
280
|
}
|
|
284
281
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Message,
|
|
1
|
+
import { Message, PersonaPayload, PersonaProtocolBaseConfig, ProtocolStatus, Session } from '../types';
|
|
2
2
|
import { PersonaProtocolBase } from './base';
|
|
3
3
|
|
|
4
4
|
type PersonaWebSocketProtocolConfig = PersonaProtocolBaseConfig & {
|
|
@@ -56,14 +56,8 @@ class PersonaWebSocketProtocol extends PersonaProtocolBase {
|
|
|
56
56
|
this.setStatus('connected');
|
|
57
57
|
});
|
|
58
58
|
this.webSocket.addEventListener('message', (event) => {
|
|
59
|
-
const data = JSON.parse(event.data) as
|
|
60
|
-
|
|
61
|
-
if (data.type !== 'message') {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const message = data.payload as PersonaMessage & { thought?: string };
|
|
65
|
-
|
|
66
|
-
this.notifyMessage(message?.thought ? { role: 'assistant', type: 'reasoning', text: message.thought } : message);
|
|
59
|
+
const data = JSON.parse(event.data) as PersonaPayload;
|
|
60
|
+
this.notifyMessage(data);
|
|
67
61
|
});
|
|
68
62
|
this.webSocket.addEventListener('close', () => {
|
|
69
63
|
this.setStatus('disconnected');
|
package/src/runtime.tsx
CHANGED
|
@@ -3,16 +3,20 @@ import { useExternalStoreRuntime, AppendMessage, AssistantRuntimeProvider } from
|
|
|
3
3
|
import {
|
|
4
4
|
PersonaConfig,
|
|
5
5
|
PersonaMessage,
|
|
6
|
+
PersonaPayload,
|
|
6
7
|
PersonaProtocol,
|
|
7
8
|
PersonaProtocolBaseConfig,
|
|
8
9
|
PersonaResponse,
|
|
10
|
+
PersonaTransaction,
|
|
9
11
|
ProtocolStatus,
|
|
10
12
|
Session,
|
|
11
13
|
} from './types';
|
|
12
14
|
import { parseMessages, convertMessage } from './messages';
|
|
13
15
|
import {
|
|
16
|
+
PersonaPersistableTransaction,
|
|
14
17
|
PersonaRESTProtocol,
|
|
15
18
|
PersonaRESTProtocolConfig,
|
|
19
|
+
PersonaTransactionProtocol,
|
|
16
20
|
PersonaWebRTCProtocol,
|
|
17
21
|
PersonaWebRTCProtocolConfig,
|
|
18
22
|
PersonaWebSocketProtocol,
|
|
@@ -49,7 +53,7 @@ function PersonaRuntimeProviderInner({
|
|
|
49
53
|
const baseEndpoint = dev ? 'localhost:8000' : 'persona.applica.guru/api';
|
|
50
54
|
const baseEndpointProtocol = dev ? 'http' : 'https';
|
|
51
55
|
const baseWebSocketProtocol = dev ? 'ws' : 'wss';
|
|
52
|
-
|
|
56
|
+
let availableProtocols = Object.keys(_protocols).map((key) => {
|
|
53
57
|
switch (key) {
|
|
54
58
|
case 'rest':
|
|
55
59
|
const restConfig: PersonaProtocolBaseConfig | true | undefined = _protocols[key];
|
|
@@ -88,6 +92,20 @@ function PersonaRuntimeProviderInner({
|
|
|
88
92
|
throw new Error(`Unknown protocol: ${key}`);
|
|
89
93
|
}
|
|
90
94
|
});
|
|
95
|
+
|
|
96
|
+
if (config.tools) {
|
|
97
|
+
availableProtocols.push(
|
|
98
|
+
new PersonaTransactionProtocol({
|
|
99
|
+
apiUrl: `${baseEndpointProtocol}://${baseEndpoint}`,
|
|
100
|
+
apiKey: config.apiKey,
|
|
101
|
+
agentId: config.agentId,
|
|
102
|
+
onTransaction: async (transaction: PersonaPersistableTransaction) => {
|
|
103
|
+
await transaction.invoke(config.tools!);
|
|
104
|
+
},
|
|
105
|
+
logger,
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
91
109
|
return availableProtocols;
|
|
92
110
|
}
|
|
93
111
|
throw new Error('Invalid protocols configuration');
|
|
@@ -109,8 +127,15 @@ function PersonaRuntimeProviderInner({
|
|
|
109
127
|
protocolsStatus.set(protocol.getName(), status);
|
|
110
128
|
setProtocolsStatus(new Map(protocolsStatus));
|
|
111
129
|
});
|
|
112
|
-
protocol.addMessageListener((message:
|
|
113
|
-
|
|
130
|
+
protocol.addMessageListener((message: PersonaPayload) => {
|
|
131
|
+
if (message.type === 'message') {
|
|
132
|
+
const personaMessage = message.payload as PersonaMessage;
|
|
133
|
+
setMessages((currentConversation) =>
|
|
134
|
+
parseMessages([...currentConversation, ...[{ ...personaMessage, protocol: protocol.getName() }]]),
|
|
135
|
+
);
|
|
136
|
+
} else if (message.type === 'transaction') {
|
|
137
|
+
protocols.filter((p) => p !== protocol).forEach((p) => p.onTransaction(message.payload as PersonaTransaction));
|
|
138
|
+
}
|
|
114
139
|
});
|
|
115
140
|
if (protocol.autostart && protocol.status === 'disconnected') {
|
|
116
141
|
logger?.debug(`Connecting to protocol: ${protocol.getName()}`);
|
|
@@ -206,9 +231,22 @@ function usePersonaRuntimeProtocol(protocol: string): PersonaProtocol | null {
|
|
|
206
231
|
};
|
|
207
232
|
}
|
|
208
233
|
|
|
234
|
+
function usePersonaRuntimeEndpoint(): string {
|
|
235
|
+
const context = useContext(PersonaRuntimeContext);
|
|
236
|
+
if (!context) {
|
|
237
|
+
throw new Error('usePersonaRuntimeEndpoint must be used within a PersonaRuntimeProvider');
|
|
238
|
+
}
|
|
239
|
+
for (const protocol of context.protocols) {
|
|
240
|
+
if (protocol.getName() === 'rest') {
|
|
241
|
+
return (protocol as PersonaRESTProtocol).config.apiUrl;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
throw new Error('REST protocol not found');
|
|
245
|
+
}
|
|
246
|
+
|
|
209
247
|
function usePersonaRuntimeWebRTCProtocol(): PersonaWebRTCProtocol | null {
|
|
210
248
|
return usePersonaRuntimeProtocol('webrtc') as PersonaWebRTCProtocol;
|
|
211
249
|
}
|
|
212
250
|
|
|
213
|
-
export { PersonaRuntimeProvider, usePersonaRuntime, usePersonaRuntimeProtocol, usePersonaRuntimeWebRTCProtocol };
|
|
251
|
+
export { PersonaRuntimeProvider, usePersonaRuntimeEndpoint, usePersonaRuntime, usePersonaRuntimeProtocol, usePersonaRuntimeWebRTCProtocol };
|
|
214
252
|
export type { PersonaMessage, PersonaResponse };
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PersonaTools } from 'src/protocol';
|
|
1
2
|
import { PersonaLogger } from './logging';
|
|
2
3
|
import { ReactNode } from 'react';
|
|
3
4
|
|
|
@@ -17,18 +18,60 @@ export type FunctionResponse = {
|
|
|
17
18
|
function_call_id: string;
|
|
18
19
|
};
|
|
19
20
|
|
|
21
|
+
export type PersonaFile = {
|
|
22
|
+
name: string;
|
|
23
|
+
url: string;
|
|
24
|
+
contentType: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type PersonaSession = {
|
|
28
|
+
id: string;
|
|
29
|
+
code: string;
|
|
30
|
+
projectId: string;
|
|
31
|
+
agentId: string;
|
|
32
|
+
currentAgentId: string;
|
|
33
|
+
userId: string;
|
|
34
|
+
status: 'active' | 'finished';
|
|
35
|
+
type: 'realtime' | 'async';
|
|
36
|
+
createdAt: string;
|
|
37
|
+
finishedAt: string | null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type PersonaAgentContext = {
|
|
41
|
+
data: unknown;
|
|
42
|
+
files: PersonaFile[];
|
|
43
|
+
session: PersonaSession;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type PersonaTransaction = {
|
|
47
|
+
id: string;
|
|
48
|
+
projectId: string;
|
|
49
|
+
sessionId: string;
|
|
50
|
+
status: 'pending' | 'completed' | 'failed';
|
|
51
|
+
error: string | null;
|
|
52
|
+
functionCall: FunctionCall | null;
|
|
53
|
+
context: PersonaAgentContext;
|
|
54
|
+
};
|
|
55
|
+
|
|
20
56
|
export type PersonaMessage = {
|
|
21
57
|
id?: string | null;
|
|
22
58
|
protocol?: string;
|
|
59
|
+
thought?: string;
|
|
23
60
|
text: string;
|
|
24
|
-
type: 'reasoning' | 'text';
|
|
61
|
+
type: 'reasoning' | 'text' | 'transaction';
|
|
25
62
|
role: 'user' | 'assistant' | 'function';
|
|
63
|
+
files?: PersonaFile[];
|
|
26
64
|
sessionId?: string;
|
|
27
65
|
finishReason?: 'stop' | 'function_call';
|
|
28
66
|
functionCalls?: FunctionCall[];
|
|
29
67
|
functionResponse?: FunctionResponse;
|
|
30
68
|
};
|
|
31
69
|
|
|
70
|
+
export type PersonaPayload = {
|
|
71
|
+
type: 'message' | 'transaction';
|
|
72
|
+
payload: PersonaMessage | PersonaTransaction;
|
|
73
|
+
};
|
|
74
|
+
|
|
32
75
|
export type ModelResponse = {
|
|
33
76
|
messages: PersonaMessage[];
|
|
34
77
|
};
|
|
@@ -51,7 +94,7 @@ export type PersonaWebRTCConfig = PersonaBaseConfig & {
|
|
|
51
94
|
export type Session = string | null | undefined;
|
|
52
95
|
export type Message = string;
|
|
53
96
|
|
|
54
|
-
export type MessageListenerCallback = (message:
|
|
97
|
+
export type MessageListenerCallback = (message: PersonaPayload) => void;
|
|
55
98
|
export type StatusChangeCallback = (status: ProtocolStatus) => void;
|
|
56
99
|
|
|
57
100
|
export type ProtocolStatus = 'disconnected' | 'connecting' | 'connected';
|
|
@@ -71,6 +114,8 @@ export interface PersonaProtocol {
|
|
|
71
114
|
connect: (session?: Session) => Promise<Session>;
|
|
72
115
|
disconnect: () => Promise<void>;
|
|
73
116
|
|
|
117
|
+
onTransaction: (transaction: PersonaTransaction) => void;
|
|
118
|
+
|
|
74
119
|
setSession: (session: Session) => Promise<void>;
|
|
75
120
|
|
|
76
121
|
send: (message: Message) => Promise<void>;
|
|
@@ -95,4 +140,6 @@ export type PersonaConfig = PersonaBaseConfig &
|
|
|
95
140
|
webrtc?: PersonaProtocolBaseConfig | true;
|
|
96
141
|
websocket?: PersonaProtocolBaseConfig | true;
|
|
97
142
|
};
|
|
143
|
+
|
|
144
|
+
tools?: PersonaTools;
|
|
98
145
|
};
|