@applica-software-guru/persona-chat-sdk 0.1.102
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/.eslintrc.cjs +11 -0
- package/.github/copilot-instructions.md +3 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +8 -0
- package/CLAUDE.md +3 -0
- package/GEMINI.md +3 -0
- package/README.md +33 -0
- package/bitbucket-pipelines.yml +19 -0
- package/components.json +24 -0
- package/dist/bundle.cjs.js +28 -0
- package/dist/bundle.cjs.js.map +1 -0
- package/dist/bundle.es.js +5173 -0
- package/dist/bundle.es.js.map +1 -0
- package/dist/bundle.iife.js +28 -0
- package/dist/bundle.iife.js.map +1 -0
- package/dist/bundle.umd.js +28 -0
- package/dist/bundle.umd.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/logging.d.ts +18 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/messages.d.ts +9 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/projects.d.ts +17 -0
- package/dist/projects.d.ts.map +1 -0
- package/dist/protocol/base.d.ts +25 -0
- package/dist/protocol/base.d.ts.map +1 -0
- package/dist/protocol/index.d.ts +6 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/rest.d.ts +25 -0
- package/dist/protocol/rest.d.ts.map +1 -0
- package/dist/protocol/transaction.d.ts +56 -0
- package/dist/protocol/transaction.d.ts.map +1 -0
- package/dist/protocol/webrtc.d.ts +60 -0
- package/dist/protocol/webrtc.d.ts.map +1 -0
- package/dist/protocol/websocket.d.ts +22 -0
- package/dist/protocol/websocket.d.ts.map +1 -0
- package/dist/runtime/context.d.ts +34 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/file-attachment-adapter.d.ts +15 -0
- package/dist/runtime/file-attachment-adapter.d.ts.map +1 -0
- package/dist/runtime/handlers.d.ts +21 -0
- package/dist/runtime/handlers.d.ts.map +1 -0
- package/dist/runtime/listeners.d.ts +6 -0
- package/dist/runtime/listeners.d.ts.map +1 -0
- package/dist/runtime/protocols.d.ts +17 -0
- package/dist/runtime/protocols.d.ts.map +1 -0
- package/dist/runtime/threads.d.ts +34 -0
- package/dist/runtime/threads.d.ts.map +1 -0
- package/dist/runtime/utils.d.ts +10 -0
- package/dist/runtime/utils.d.ts.map +1 -0
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/storage/base.d.ts +9 -0
- package/dist/storage/base.d.ts.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/persona.d.ts +29 -0
- package/dist/storage/persona.d.ts.map +1 -0
- package/dist/tools.d.ts +72 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/types.d.ts +237 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/README.md +13 -0
- package/docs/api-reference.md +16 -0
- package/docs/contributing.md +21 -0
- package/docs/customization.md +74 -0
- package/docs/features.md +7 -0
- package/docs/installation.md +9 -0
- package/docs/messages.md +214 -0
- package/docs/protocols.md +39 -0
- package/docs/transactions.md +121 -0
- package/docs/usage.md +40 -0
- package/jsconfig.node.json +10 -0
- package/package.json +82 -0
- package/playground/index.html +14 -0
- package/playground/src/app.tsx +10 -0
- package/playground/src/chat.tsx +117 -0
- package/playground/src/components/assistant-ui/assistant-modal.tsx +57 -0
- package/playground/src/components/assistant-ui/attachment.tsx +197 -0
- package/playground/src/components/assistant-ui/markdown-text.tsx +228 -0
- package/playground/src/components/assistant-ui/thread-list.tsx +91 -0
- package/playground/src/components/assistant-ui/thread.tsx +302 -0
- package/playground/src/components/assistant-ui/threadlist-sidebar.tsx +59 -0
- package/playground/src/components/assistant-ui/tool-fallback.tsx +93 -0
- package/playground/src/components/assistant-ui/tooltip-icon-button.tsx +42 -0
- package/playground/src/components/chat/logging.tsx +53 -0
- package/playground/src/components/ui/avatar.tsx +51 -0
- package/playground/src/components/ui/button.tsx +60 -0
- package/playground/src/components/ui/dialog.tsx +141 -0
- package/playground/src/components/ui/input.tsx +21 -0
- package/playground/src/components/ui/separator.tsx +26 -0
- package/playground/src/components/ui/sheet.tsx +139 -0
- package/playground/src/components/ui/sidebar.tsx +619 -0
- package/playground/src/components/ui/skeleton.tsx +13 -0
- package/playground/src/components/ui/tooltip.tsx +59 -0
- package/playground/src/hooks/theme.ts +70 -0
- package/playground/src/hooks/use-mobile.ts +19 -0
- package/playground/src/lib/utils.ts +6 -0
- package/playground/src/main.tsx +10 -0
- package/playground/src/styles.css +120 -0
- package/playground/src/tools.ts +149 -0
- package/playground/src/vite-env.d.ts +1 -0
- package/preview-build.sh +23 -0
- package/src/index.ts +7 -0
- package/src/logging.ts +34 -0
- package/src/messages.ts +202 -0
- package/src/projects.ts +57 -0
- package/src/protocol/base.ts +73 -0
- package/src/protocol/index.ts +5 -0
- package/src/protocol/rest.ts +107 -0
- package/src/protocol/transaction.ts +182 -0
- package/src/protocol/webrtc.ts +379 -0
- package/src/protocol/websocket.ts +111 -0
- package/src/runtime/context.ts +88 -0
- package/src/runtime/file-attachment-adapter.ts +48 -0
- package/src/runtime/handlers.ts +322 -0
- package/src/runtime/index.ts +6 -0
- package/src/runtime/listeners.ts +79 -0
- package/src/runtime/protocols.ts +169 -0
- package/src/runtime/threads.ts +105 -0
- package/src/runtime/utils.ts +46 -0
- package/src/runtime.tsx +334 -0
- package/src/storage/base.ts +13 -0
- package/src/storage/index.ts +2 -0
- package/src/storage/persona.ts +138 -0
- package/src/tools.ts +211 -0
- package/src/types.ts +284 -0
- package/tsconfig.json +36 -0
- package/tsconfig.node.json +15 -0
- package/vite.config.ts +74 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
type Theme = 'light' | 'dark' | 'auto';
|
|
4
|
+
const STORAGE_KEY = 'persona_theme_preference';
|
|
5
|
+
|
|
6
|
+
function getSystemTheme(): 'light' | 'dark' {
|
|
7
|
+
if (typeof window === 'undefined' || !window.matchMedia) return 'light';
|
|
8
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useColorScheme() {
|
|
12
|
+
const [theme, setTheme] = useState<Theme>(() => {
|
|
13
|
+
try {
|
|
14
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
15
|
+
return (stored as Theme) || 'auto';
|
|
16
|
+
} catch {
|
|
17
|
+
return 'auto';
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const apply = useCallback((t: Theme) => {
|
|
22
|
+
const root = document.documentElement;
|
|
23
|
+
const effective = t === 'auto' ? getSystemTheme() : t;
|
|
24
|
+
if (effective === 'dark') root.classList.add('dark');
|
|
25
|
+
else root.classList.remove('dark');
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
// Apply on mount and whenever theme changes
|
|
30
|
+
apply(theme);
|
|
31
|
+
|
|
32
|
+
// Listen to system changes when in 'auto' mode
|
|
33
|
+
let mql: MediaQueryList | null = null;
|
|
34
|
+
const onChange = () => {
|
|
35
|
+
if (theme === 'auto') apply('auto');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
39
|
+
mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
40
|
+
if (mql.addEventListener) mql.addEventListener('change', onChange);
|
|
41
|
+
else mql.addListener(onChange as any);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return () => {
|
|
45
|
+
if (!mql) return;
|
|
46
|
+
if (mql.removeEventListener) mql.removeEventListener('change', onChange);
|
|
47
|
+
else mql.removeListener(onChange as any);
|
|
48
|
+
};
|
|
49
|
+
}, [theme, apply]);
|
|
50
|
+
|
|
51
|
+
const setThemeAndPersist = useCallback((t: Theme) => {
|
|
52
|
+
try {
|
|
53
|
+
localStorage.setItem(STORAGE_KEY, t);
|
|
54
|
+
} catch {}
|
|
55
|
+
setTheme(t);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const toggle = useCallback(() => {
|
|
59
|
+
console.log('Toggling theme from');
|
|
60
|
+
setTheme((prev) => {
|
|
61
|
+
const next = prev === 'dark' ? 'light' : prev === 'light' ? 'auto' : 'dark';
|
|
62
|
+
try {
|
|
63
|
+
localStorage.setItem(STORAGE_KEY, next);
|
|
64
|
+
} catch {}
|
|
65
|
+
return next;
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
return { theme, setTheme: setThemeAndPersist, toggle } as const;
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener("change", onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener("change", onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
8
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
9
|
+
--radius-lg: var(--radius);
|
|
10
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
11
|
+
--color-background: var(--background);
|
|
12
|
+
--color-foreground: var(--foreground);
|
|
13
|
+
--color-card: var(--card);
|
|
14
|
+
--color-card-foreground: var(--card-foreground);
|
|
15
|
+
--color-popover: var(--popover);
|
|
16
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
17
|
+
--color-primary: var(--primary);
|
|
18
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
19
|
+
--color-secondary: var(--secondary);
|
|
20
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
21
|
+
--color-muted: var(--muted);
|
|
22
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
23
|
+
--color-accent: var(--accent);
|
|
24
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
25
|
+
--color-destructive: var(--destructive);
|
|
26
|
+
--color-border: var(--border);
|
|
27
|
+
--color-input: var(--input);
|
|
28
|
+
--color-ring: var(--ring);
|
|
29
|
+
--color-chart-1: var(--chart-1);
|
|
30
|
+
--color-chart-2: var(--chart-2);
|
|
31
|
+
--color-chart-3: var(--chart-3);
|
|
32
|
+
--color-chart-4: var(--chart-4);
|
|
33
|
+
--color-chart-5: var(--chart-5);
|
|
34
|
+
--color-sidebar: var(--sidebar);
|
|
35
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
36
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
37
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
38
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
39
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
40
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
41
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:root {
|
|
45
|
+
--radius: 0.625rem;
|
|
46
|
+
--background: oklch(1 0 0);
|
|
47
|
+
--foreground: oklch(0.145 0 0);
|
|
48
|
+
--card: oklch(1 0 0);
|
|
49
|
+
--card-foreground: oklch(0.145 0 0);
|
|
50
|
+
--popover: oklch(1 0 0);
|
|
51
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
52
|
+
--primary: oklch(0.205 0 0);
|
|
53
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
54
|
+
--secondary: oklch(0.97 0 0);
|
|
55
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
56
|
+
--muted: oklch(0.97 0 0);
|
|
57
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
58
|
+
--accent: oklch(0.97 0 0);
|
|
59
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
60
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
61
|
+
--border: oklch(0.922 0 0);
|
|
62
|
+
--input: oklch(0.922 0 0);
|
|
63
|
+
--ring: oklch(0.708 0 0);
|
|
64
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
65
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
66
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
67
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
68
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
69
|
+
--sidebar: oklch(0.985 0 0);
|
|
70
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
71
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
72
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
73
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
74
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
75
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
76
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.dark {
|
|
80
|
+
--background: oklch(0.145 0 0);
|
|
81
|
+
--foreground: oklch(0.985 0 0);
|
|
82
|
+
--card: oklch(0.205 0 0);
|
|
83
|
+
--card-foreground: oklch(0.985 0 0);
|
|
84
|
+
--popover: oklch(0.205 0 0);
|
|
85
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
86
|
+
--primary: oklch(0.922 0 0);
|
|
87
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
88
|
+
--secondary: oklch(0.269 0 0);
|
|
89
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
90
|
+
--muted: oklch(0.269 0 0);
|
|
91
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
92
|
+
--accent: oklch(0.269 0 0);
|
|
93
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
94
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
95
|
+
--border: oklch(1 0 0 / 10%);
|
|
96
|
+
--input: oklch(1 0 0 / 15%);
|
|
97
|
+
--ring: oklch(0.556 0 0);
|
|
98
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
99
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
100
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
101
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
102
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
103
|
+
--sidebar: oklch(0.205 0 0);
|
|
104
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
105
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
106
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
107
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
108
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
110
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@layer base {
|
|
114
|
+
* {
|
|
115
|
+
@apply border-border outline-ring/50;
|
|
116
|
+
}
|
|
117
|
+
body {
|
|
118
|
+
@apply bg-background text-foreground;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
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-chat-sdk';
|
|
11
|
+
|
|
12
|
+
const toolLibrary = [
|
|
13
|
+
createTool({
|
|
14
|
+
name: 'set_field_value',
|
|
15
|
+
title: 'Set Field Value',
|
|
16
|
+
description: 'Sets the value of a specified field in the current form.',
|
|
17
|
+
parameters: {
|
|
18
|
+
name: { type: 'string', description: 'The name of the field to set', required: true },
|
|
19
|
+
value: { type: 'string', description: 'The value to set for the field', required: true },
|
|
20
|
+
},
|
|
21
|
+
output: {
|
|
22
|
+
success: { type: 'boolean', description: 'Indicates if the field value was set successfully' },
|
|
23
|
+
},
|
|
24
|
+
implementation: async (name: string, value: string) => {
|
|
25
|
+
console.log({ name, value });
|
|
26
|
+
const field = document.querySelector(`[name="${name}"]`);
|
|
27
|
+
if (field) {
|
|
28
|
+
(field as HTMLInputElement).value = value;
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
31
|
+
console.error(`Field with name "${name}" not found.`);
|
|
32
|
+
return { success: false };
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
createTool({
|
|
36
|
+
name: 'sum',
|
|
37
|
+
title: 'Sum two numbers',
|
|
38
|
+
description: 'Sums two numbers and returns the result.',
|
|
39
|
+
parameters: {
|
|
40
|
+
a: { type: 'number', description: 'First number to sum', required: true },
|
|
41
|
+
b: { type: 'number', description: 'Second number to sum', required: true },
|
|
42
|
+
},
|
|
43
|
+
output: {
|
|
44
|
+
result: { type: 'number', description: 'Sum of the two numbers' },
|
|
45
|
+
},
|
|
46
|
+
implementation: async (a: number, b: number) => {
|
|
47
|
+
const result = a + b;
|
|
48
|
+
return { result };
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
createTool({
|
|
52
|
+
name: 'concat',
|
|
53
|
+
title: 'Concatenate two strings',
|
|
54
|
+
description: 'Concatenates two strings and returns the result.',
|
|
55
|
+
parameters: {
|
|
56
|
+
str1: { type: 'string', description: 'First string to concatenate', required: true },
|
|
57
|
+
str2: { type: 'string', description: 'Second string to concatenate', required: true },
|
|
58
|
+
},
|
|
59
|
+
output: {
|
|
60
|
+
result: { type: 'string', description: 'Concatenated result of the two strings' },
|
|
61
|
+
},
|
|
62
|
+
implementation: async (str1: string, str2: string) => {
|
|
63
|
+
const result = str1 + str2;
|
|
64
|
+
return { result };
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
// User agent tool:
|
|
68
|
+
createTool({
|
|
69
|
+
name: 'userAgent',
|
|
70
|
+
title: 'Get User Agent',
|
|
71
|
+
description: 'Retrieves the user agent string from the client.',
|
|
72
|
+
parameters: {},
|
|
73
|
+
output: {
|
|
74
|
+
result: { type: 'string', description: 'User agent string' },
|
|
75
|
+
},
|
|
76
|
+
implementation: async () => {
|
|
77
|
+
return { result: navigator.userAgent };
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
// Navigate to URL tool:
|
|
81
|
+
createTool({
|
|
82
|
+
name: 'navigateTo',
|
|
83
|
+
title: 'Navigate to URL',
|
|
84
|
+
description: 'Navigates the browser to a specified URL.',
|
|
85
|
+
parameters: {
|
|
86
|
+
url: { type: 'string', description: 'The URL to navigate to', required: true },
|
|
87
|
+
},
|
|
88
|
+
output: {
|
|
89
|
+
success: { type: 'boolean', description: 'Indicates if navigation was successful' },
|
|
90
|
+
},
|
|
91
|
+
implementation: async (url: string) => {
|
|
92
|
+
window.location.href = url;
|
|
93
|
+
return { success: true };
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
// Get current URL tool:
|
|
97
|
+
createTool({
|
|
98
|
+
name: 'getCurrentUrl',
|
|
99
|
+
title: 'Get Current URL',
|
|
100
|
+
description: 'Retrieves the current browser URL.',
|
|
101
|
+
parameters: {},
|
|
102
|
+
output: {
|
|
103
|
+
url: { type: 'string', description: 'Current browser URL' },
|
|
104
|
+
},
|
|
105
|
+
implementation: async () => {
|
|
106
|
+
return { url: window.location.href };
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
|
|
110
|
+
// Current client date and time tool:
|
|
111
|
+
createTool({
|
|
112
|
+
name: 'getCurrentDateTime',
|
|
113
|
+
title: 'Get Current Date and Time',
|
|
114
|
+
description: 'Retrieves the current date and time from the client.',
|
|
115
|
+
parameters: {},
|
|
116
|
+
output: {
|
|
117
|
+
dateTime: { type: 'string', description: 'Current date and time in ISO format' },
|
|
118
|
+
},
|
|
119
|
+
implementation: async () => {
|
|
120
|
+
return { dateTime: new Date().toISOString() };
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const tools: PersonaTools = {
|
|
126
|
+
sum: function (a: number, b: number) {
|
|
127
|
+
const result = a + b;
|
|
128
|
+
return { result };
|
|
129
|
+
},
|
|
130
|
+
concat: function (str1: string, str2: string) {
|
|
131
|
+
const result = str1 + str2;
|
|
132
|
+
return { result };
|
|
133
|
+
},
|
|
134
|
+
navigateTo: function (url: string) {
|
|
135
|
+
window.location.href = url;
|
|
136
|
+
return { success: true };
|
|
137
|
+
},
|
|
138
|
+
getUserAgent: function () {
|
|
139
|
+
return { result: navigator.userAgent };
|
|
140
|
+
},
|
|
141
|
+
getCurrentUrl: function () {
|
|
142
|
+
return { url: window.location.href };
|
|
143
|
+
},
|
|
144
|
+
getCurrentDateTime: function () {
|
|
145
|
+
return { dateTime: new Date().toISOString() };
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export { toolLibrary, tools };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/preview-build.sh
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This script automates the process of building and publishing a preview version of the npm package.
|
|
3
|
+
# When executed as ./preview.sh <tag-name>, it creates and publishes a new version of the package
|
|
4
|
+
# with the format @applica-software-guru/persona-chat-sdk@0.1.0-<tag-name>.
|
|
5
|
+
# The script updates the package version with the specified tag, builds the project,
|
|
6
|
+
# and publishes it to the npm registry with the given preview tag.
|
|
7
|
+
# Usage: ./preview.sh <tag-name>
|
|
8
|
+
# Example: ./preview.sh beta
|
|
9
|
+
|
|
10
|
+
if [ -z "$1" ]; then
|
|
11
|
+
echo "Usage: $0 <preview-tag>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
PREVIEW_TAG=$1
|
|
16
|
+
|
|
17
|
+
echo "Creating preview version with tag: $PREVIEW_TAG"
|
|
18
|
+
|
|
19
|
+
npm run build
|
|
20
|
+
|
|
21
|
+
npm --no-git-tag-version version "$PREVIEW_TAG" -m "$PREVIEW_TAG"
|
|
22
|
+
|
|
23
|
+
npm publish --access public
|
package/src/index.ts
ADDED
package/src/logging.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface PersonaLogger {
|
|
2
|
+
log: (message: string, ...args: unknown[]) => void;
|
|
3
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
4
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
5
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
6
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class PersonaConsoleLogger implements PersonaLogger {
|
|
10
|
+
prefix = '[Persona]';
|
|
11
|
+
|
|
12
|
+
log(message: string, ...args: unknown[]) {
|
|
13
|
+
console.log(`${this.prefix} - ${message}`, ...args);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
info(message: string, ...args: unknown[]) {
|
|
17
|
+
console.info(`${this.prefix} - ${message}`, ...args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
warn(message: string, ...args: unknown[]) {
|
|
21
|
+
console.warn(`${this.prefix} - ${message}`, ...args);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
error(message: string, ...args: unknown[]) {
|
|
25
|
+
console.error(`${this.prefix} - ${message}`, ...args);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
debug(message: string, ...args: unknown[]) {
|
|
29
|
+
console.debug(`${this.prefix} - ${message}`, ...args);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { PersonaConsoleLogger };
|
|
34
|
+
export type { PersonaLogger };
|
package/src/messages.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { PersonaMessage } from './types';
|
|
2
|
+
import { FileMessagePart, ThreadMessageLike, MessageStatus } from '@assistant-ui/react';
|
|
3
|
+
|
|
4
|
+
let messageIdCounter = 0;
|
|
5
|
+
|
|
6
|
+
export function generateMessageId(): string {
|
|
7
|
+
return `msg_${Date.now()}_${messageIdCounter++}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ensureMessageId(message: PersonaMessage): PersonaMessage {
|
|
11
|
+
// Convert thought to reasoning type
|
|
12
|
+
if (message.thought) {
|
|
13
|
+
message = { ...message, type: 'reasoning' };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!message.id) {
|
|
17
|
+
return {
|
|
18
|
+
...message,
|
|
19
|
+
id: generateMessageId(),
|
|
20
|
+
createdAt: message.createdAt || new Date(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (!message.createdAt) {
|
|
24
|
+
return { ...message, createdAt: new Date() };
|
|
25
|
+
}
|
|
26
|
+
return message;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function removeEmptyMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
30
|
+
return messages.filter((message) => {
|
|
31
|
+
if (message.role === 'assistant' && !message.functionCalls && !message.file && !message.image) {
|
|
32
|
+
if (!message.text || message.text.trim() === '') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function parseMessages(messages: PersonaMessage[]): PersonaMessage[] {
|
|
40
|
+
const outputMessages: PersonaMessage[] = [];
|
|
41
|
+
let currentMessage: PersonaMessage | null = null;
|
|
42
|
+
|
|
43
|
+
for (let message of messages) {
|
|
44
|
+
// Ensure every message has an ID
|
|
45
|
+
message = ensureMessageId(message);
|
|
46
|
+
|
|
47
|
+
if (message.type === 'transaction') {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (message.file) {
|
|
52
|
+
if (currentMessage) {
|
|
53
|
+
outputMessages.push(currentMessage);
|
|
54
|
+
currentMessage = null;
|
|
55
|
+
}
|
|
56
|
+
outputMessages.push(message);
|
|
57
|
+
continue;
|
|
58
|
+
} else if (message.functionCalls) {
|
|
59
|
+
if (currentMessage) {
|
|
60
|
+
// Drop empty assistant messages that precede function calls (streaming artifacts)
|
|
61
|
+
const isEmptyAssistant =
|
|
62
|
+
currentMessage.role === 'assistant' &&
|
|
63
|
+
(!currentMessage.text || currentMessage.text.trim() === '') &&
|
|
64
|
+
!currentMessage.file &&
|
|
65
|
+
!currentMessage.image;
|
|
66
|
+
if (!isEmptyAssistant) {
|
|
67
|
+
outputMessages.push(currentMessage);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
outputMessages.push(message);
|
|
71
|
+
currentMessage = null;
|
|
72
|
+
} else if (message.functionResponse) {
|
|
73
|
+
outputMessages[outputMessages.length - 1] = {
|
|
74
|
+
...outputMessages[outputMessages.length - 1],
|
|
75
|
+
functionResponse: message.functionResponse,
|
|
76
|
+
};
|
|
77
|
+
} else if (
|
|
78
|
+
currentMessage &&
|
|
79
|
+
message.type === currentMessage.type &&
|
|
80
|
+
message.protocol === currentMessage.protocol &&
|
|
81
|
+
currentMessage.role === message.role &&
|
|
82
|
+
// CRITICAL: Only merge if the new message has the same ID (streaming chunks)
|
|
83
|
+
// OR if the current message doesn't have a finishReason yet (still streaming)
|
|
84
|
+
// This preserves branches while allowing streaming to work
|
|
85
|
+
(message.id === currentMessage.id || !currentMessage.finishReason)
|
|
86
|
+
) {
|
|
87
|
+
// Merge streaming chunks
|
|
88
|
+
currentMessage.text += message.text;
|
|
89
|
+
currentMessage.file = currentMessage.file || message.file;
|
|
90
|
+
currentMessage.finishReason = message.finishReason || currentMessage.finishReason;
|
|
91
|
+
// Keep the ID of the first chunk
|
|
92
|
+
currentMessage.id = currentMessage.id || message.id;
|
|
93
|
+
currentMessage.sources = currentMessage.sources ? [...(currentMessage.sources || []), ...(message.sources || [])] : message.sources;
|
|
94
|
+
} else {
|
|
95
|
+
// Different message or current is complete - don't merge
|
|
96
|
+
if (currentMessage) {
|
|
97
|
+
outputMessages.push(currentMessage);
|
|
98
|
+
}
|
|
99
|
+
currentMessage = {
|
|
100
|
+
...message,
|
|
101
|
+
text: message.file ? '' : message.text,
|
|
102
|
+
finishReason: message.file ? undefined : message.finishReason,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (currentMessage) {
|
|
108
|
+
outputMessages.push(currentMessage);
|
|
109
|
+
}
|
|
110
|
+
const cleanMessages = removeEmptyMessages(outputMessages);
|
|
111
|
+
return cleanMessages;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function convertMessage(message: PersonaMessage): ThreadMessageLike {
|
|
115
|
+
const files = message.file
|
|
116
|
+
? [
|
|
117
|
+
{
|
|
118
|
+
type: 'file',
|
|
119
|
+
data: message.file.url,
|
|
120
|
+
mimeType: message.file.contentType,
|
|
121
|
+
} as FileMessagePart,
|
|
122
|
+
]
|
|
123
|
+
: [];
|
|
124
|
+
|
|
125
|
+
// Determine message status for assistant messages
|
|
126
|
+
let messageStatus: MessageStatus | undefined;
|
|
127
|
+
|
|
128
|
+
if (message.role === 'assistant' || message.role === 'function') {
|
|
129
|
+
if (message.status) {
|
|
130
|
+
// Use existing status if provided
|
|
131
|
+
messageStatus = message.status as any;
|
|
132
|
+
} else if (message.finishReason === 'stop') {
|
|
133
|
+
messageStatus = { type: 'complete', reason: 'stop' } as const;
|
|
134
|
+
} else if (message.finishReason === 'function_call') {
|
|
135
|
+
messageStatus = { type: 'complete', reason: 'stop' } as const;
|
|
136
|
+
} else if (!message.finishReason) {
|
|
137
|
+
// Message is still being generated - this will trigger optimistic updates
|
|
138
|
+
messageStatus = undefined; // Let the runtime handle in-progress state
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Handle function/tool calls
|
|
143
|
+
if (message.role === 'function') {
|
|
144
|
+
const result: ThreadMessageLike = {
|
|
145
|
+
id: message.id!,
|
|
146
|
+
role: 'assistant',
|
|
147
|
+
createdAt: message.createdAt,
|
|
148
|
+
content:
|
|
149
|
+
message.functionCalls?.map((call) => ({
|
|
150
|
+
type: 'tool-call',
|
|
151
|
+
toolName: call.name,
|
|
152
|
+
toolCallId: call.id || `tool_${Date.now()}`,
|
|
153
|
+
args: call.args,
|
|
154
|
+
result: message.functionResponse?.result,
|
|
155
|
+
})) ?? [],
|
|
156
|
+
metadata: {
|
|
157
|
+
...message.metadata,
|
|
158
|
+
custom: {
|
|
159
|
+
protocol: message.protocol,
|
|
160
|
+
sessionId: message.sessionId,
|
|
161
|
+
sources: message.sources,
|
|
162
|
+
...message.metadata?.custom,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
if (messageStatus) {
|
|
168
|
+
return { ...result, status: messageStatus };
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Handle regular messages
|
|
174
|
+
const baseMessage: ThreadMessageLike = {
|
|
175
|
+
id: message.id!,
|
|
176
|
+
role: message.role,
|
|
177
|
+
createdAt: message.createdAt,
|
|
178
|
+
content:
|
|
179
|
+
message.type === 'reasoning'
|
|
180
|
+
? [{ type: 'reasoning' as any, text: message.text }, ...files]
|
|
181
|
+
: [{ type: 'text', text: message.text }, ...files],
|
|
182
|
+
metadata: {
|
|
183
|
+
...message.metadata,
|
|
184
|
+
custom: {
|
|
185
|
+
protocol: message.protocol,
|
|
186
|
+
sessionId: message.sessionId,
|
|
187
|
+
sources: message.sources,
|
|
188
|
+
type: message.type,
|
|
189
|
+
...message.metadata?.custom,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Add status only for assistant messages
|
|
195
|
+
if (message.role === 'assistant' && messageStatus) {
|
|
196
|
+
return { ...baseMessage, status: messageStatus };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return baseMessage;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { parseMessages, convertMessage, removeEmptyMessages };
|