@rimori/react-client 0.4.15 → 0.4.16-next.0
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/dist/components/ai/Avatar.js +1 -3
- package/dist/components/ai/utils.d.ts +2 -0
- package/dist/components/ai/utils.js +1 -0
- package/dist/hooks/UseChatHook.d.ts +13 -1
- package/dist/hooks/UseChatHook.js +27 -11
- package/dist/hooks/UseSettingsHook.d.ts +22 -0
- package/dist/hooks/UseSettingsHook.js +48 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +4 -4
- package/src/components/ai/Avatar.tsx +2 -3
- package/src/components/ai/utils.ts +2 -0
- package/src/hooks/UseChatHook.ts +47 -11
- package/src/hooks/UseSettingsHook.ts +55 -0
- package/src/index.ts +1 -0
|
@@ -12,9 +12,7 @@ export function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversat
|
|
|
12
12
|
const { isDark: isDarkThemeValue } = useTheme(plugin.theme);
|
|
13
13
|
const [agentReplying, setAgentReplying] = useState(false);
|
|
14
14
|
const [isProcessingMessage, setIsProcessingMessage] = useState(false);
|
|
15
|
-
const dialectTtsInstruction = !disableDialect && (userInfo === null || userInfo === void 0 ? void 0 : userInfo.dialect)
|
|
16
|
-
? `Speak with a ${userInfo.dialect} accent and pronunciation.`
|
|
17
|
-
: undefined;
|
|
15
|
+
const dialectTtsInstruction = !disableDialect && (userInfo === null || userInfo === void 0 ? void 0 : userInfo.dialect) ? `Speak with a ${userInfo.dialect} accent and pronunciation.` : undefined;
|
|
18
16
|
const sender = useMemo(() => new MessageSender((...args) => ai.getVoice(...args), voiceId, cache), [voiceId, ai, cache]);
|
|
19
17
|
useEffect(() => {
|
|
20
18
|
sender.setInstructions(dialectTtsInstruction);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building messages client-side. */
|
|
1
2
|
export interface FirstMessages {
|
|
2
3
|
instructions?: string;
|
|
3
4
|
userMessage?: string;
|
|
4
5
|
assistantMessage?: string;
|
|
5
6
|
}
|
|
7
|
+
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building messages client-side. */
|
|
6
8
|
export declare function getFirstMessages(instructions: FirstMessages): any[];
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Tool } from '@rimori/client';
|
|
3
3
|
import { Message } from '@rimori/client';
|
|
4
|
+
export interface UseChatConfig {
|
|
5
|
+
/** Server-side prompt name (e.g. 'storytelling.story'). */
|
|
6
|
+
prompt?: string;
|
|
7
|
+
/** Variables for the server-side prompt template. */
|
|
8
|
+
promptVariables?: Record<string, any>;
|
|
9
|
+
}
|
|
4
10
|
export declare function useChat(tools?: Tool[], options?: {
|
|
5
|
-
|
|
11
|
+
/** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
|
|
12
|
+
knowledgeId?: string;
|
|
13
|
+
/** Server-side prompt name (e.g. 'storytelling.story'). When set, the backend resolves the system prompt. */
|
|
14
|
+
prompt?: string;
|
|
15
|
+
/** Variables for the server-side prompt template. */
|
|
16
|
+
promptVariables?: Record<string, any>;
|
|
6
17
|
}): {
|
|
7
18
|
messages: Message[];
|
|
8
19
|
append: (appendMessages: Message[]) => void;
|
|
20
|
+
init: (config: UseChatConfig) => void;
|
|
9
21
|
isLoading: boolean;
|
|
10
22
|
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
11
23
|
lastMessage: Message | undefined;
|
|
@@ -4,25 +4,41 @@ export function useChat(tools, options) {
|
|
|
4
4
|
const [messages, setMessages] = React.useState([]);
|
|
5
5
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
6
6
|
const { ai } = useRimori();
|
|
7
|
+
const configRef = React.useRef({
|
|
8
|
+
prompt: options === null || options === void 0 ? void 0 : options.prompt,
|
|
9
|
+
promptVariables: options === null || options === void 0 ? void 0 : options.promptVariables,
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Set (or update) the prompt configuration after mount.
|
|
13
|
+
* Call this before the first `append` to configure the server-side prompt
|
|
14
|
+
* without needing refs. Does NOT trigger AI generation.
|
|
15
|
+
*/
|
|
16
|
+
const init = React.useCallback((config) => {
|
|
17
|
+
configRef.current = Object.assign(Object.assign({}, configRef.current), config);
|
|
18
|
+
}, []);
|
|
7
19
|
const append = (appendMessages) => {
|
|
20
|
+
var _a, _b;
|
|
8
21
|
const allMessages = [...messages, ...appendMessages];
|
|
9
22
|
setMessages(allMessages);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
setIsLoading(true);
|
|
24
|
+
const knowledgeId = options === null || options === void 0 ? void 0 : options.knowledgeId;
|
|
25
|
+
const promptVariables = (_a = configRef.current.promptVariables) !== null && _a !== void 0 ? _a : options === null || options === void 0 ? void 0 : options.promptVariables;
|
|
26
|
+
const prompt = (_b = configRef.current.prompt) !== null && _b !== void 0 ? _b : options === null || options === void 0 ? void 0 : options.prompt;
|
|
27
|
+
void ai.getSteamedText(allMessages, (id, message, finished, toolInvocations) => {
|
|
13
28
|
setIsLoading(!finished);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}, tools, false, undefined, knowledgeId);
|
|
29
|
+
setMessages((prev) => {
|
|
30
|
+
const last = prev[prev.length - 1];
|
|
31
|
+
if ((last === null || last === void 0 ? void 0 : last.id) === id) {
|
|
32
|
+
return [...prev.slice(0, -1), Object.assign(Object.assign({}, last), { content: message, toolCalls: toolInvocations })];
|
|
33
|
+
}
|
|
34
|
+
return [...prev, { id, role: 'assistant', content: message, toolCalls: toolInvocations }];
|
|
35
|
+
});
|
|
36
|
+
}, tools, false, undefined, knowledgeId, prompt, promptVariables);
|
|
22
37
|
};
|
|
23
38
|
return {
|
|
24
39
|
messages,
|
|
25
40
|
append,
|
|
41
|
+
init,
|
|
26
42
|
isLoading,
|
|
27
43
|
setMessages,
|
|
28
44
|
lastMessage: messages[messages.length - 1],
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BasePluginSettings, ExplicitUndefined } from '@rimori/client';
|
|
2
|
+
export interface UseSettingsResult<T extends BasePluginSettings> {
|
|
3
|
+
/** The current settings (starts as `defaultSettings`, updated once loaded from server). */
|
|
4
|
+
settings: ExplicitUndefined<T>;
|
|
5
|
+
/** True while the initial load is in progress. */
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
/** Persist a partial or full settings update. Merges with current settings. */
|
|
8
|
+
setSettings: (update: Partial<T>) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Hook for managing plugin settings with loading state.
|
|
12
|
+
*
|
|
13
|
+
* Loads settings on mount (merging with defaults via `plugin.getSettings`),
|
|
14
|
+
* exposes the current value, a loading flag, and a setter that persists changes.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { settings, isLoading, setSettings } = useSettings(DEFAULT_SETTINGS);
|
|
18
|
+
* if (isLoading) return <Spinner />;
|
|
19
|
+
* await setSettings({ genre: 'mystery' });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function useSettings<T extends BasePluginSettings>(defaultSettings: ExplicitUndefined<T>): UseSettingsResult<T>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
11
|
+
import { useRimori } from '../providers/PluginProvider';
|
|
12
|
+
/**
|
|
13
|
+
* Hook for managing plugin settings with loading state.
|
|
14
|
+
*
|
|
15
|
+
* Loads settings on mount (merging with defaults via `plugin.getSettings`),
|
|
16
|
+
* exposes the current value, a loading flag, and a setter that persists changes.
|
|
17
|
+
*
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const { settings, isLoading, setSettings } = useSettings(DEFAULT_SETTINGS);
|
|
20
|
+
* if (isLoading) return <Spinner />;
|
|
21
|
+
* await setSettings({ genre: 'mystery' });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function useSettings(defaultSettings) {
|
|
25
|
+
const plugin = useRimori();
|
|
26
|
+
const [settings, setSettingsState] = useState(defaultSettings);
|
|
27
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
void plugin.plugin.getSettings(defaultSettings).then((loaded) => {
|
|
31
|
+
if (cancelled)
|
|
32
|
+
return;
|
|
33
|
+
setSettingsState(loaded);
|
|
34
|
+
setIsLoading(false);
|
|
35
|
+
});
|
|
36
|
+
return () => {
|
|
37
|
+
cancelled = true;
|
|
38
|
+
};
|
|
39
|
+
// Only run on mount — defaultSettings reference is stable by convention.
|
|
40
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
+
}, []);
|
|
42
|
+
const setSettings = useCallback((update) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const merged = Object.assign(Object.assign({}, settings), update);
|
|
44
|
+
setSettingsState(merged);
|
|
45
|
+
yield plugin.plugin.setSettings(merged);
|
|
46
|
+
}), [settings, plugin]);
|
|
47
|
+
return { settings, isLoading, setSettings };
|
|
48
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/react-client",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.16-next.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc && sass src/style.scss:dist/style.css",
|
|
21
|
-
"dev": "tsc -w --preserveWatchOutput",
|
|
21
|
+
"dev": "git pull && tsc -w --preserveWatchOutput",
|
|
22
22
|
"dev:watch": "tsc -w --preserveWatchOutput",
|
|
23
23
|
"css-dev": "sass --watch src/style.scss:dist/style.css",
|
|
24
24
|
"lint": "eslint . --fix",
|
|
25
25
|
"format": "prettier --write ."
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@rimori/client": "^2.5.
|
|
28
|
+
"@rimori/client": "^2.5.27",
|
|
29
29
|
"react": "^18.1.0",
|
|
30
30
|
"react-dom": "^18.1.0"
|
|
31
31
|
},
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.37.0",
|
|
52
|
-
"@rimori/client": "^2.5.
|
|
52
|
+
"@rimori/client": "^2.5.27",
|
|
53
53
|
"@types/react": "^18.3.21",
|
|
54
54
|
"eslint-config-prettier": "^10.1.8",
|
|
55
55
|
"eslint-plugin-prettier": "^5.5.4",
|
|
@@ -39,9 +39,8 @@ export function Avatar({
|
|
|
39
39
|
const { isDark: isDarkThemeValue } = useTheme(plugin.theme);
|
|
40
40
|
const [agentReplying, setAgentReplying] = useState(false);
|
|
41
41
|
const [isProcessingMessage, setIsProcessingMessage] = useState(false);
|
|
42
|
-
const dialectTtsInstruction =
|
|
43
|
-
? `Speak with a ${userInfo.dialect} accent and pronunciation.`
|
|
44
|
-
: undefined;
|
|
42
|
+
const dialectTtsInstruction =
|
|
43
|
+
!disableDialect && userInfo?.dialect ? `Speak with a ${userInfo.dialect} accent and pronunciation.` : undefined;
|
|
45
44
|
const sender = useMemo(
|
|
46
45
|
() => new MessageSender((...args) => ai.getVoice(...args), voiceId, cache),
|
|
47
46
|
[voiceId, ai, cache],
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building messages client-side. */
|
|
1
2
|
export interface FirstMessages {
|
|
2
3
|
instructions?: string;
|
|
3
4
|
userMessage?: string;
|
|
4
5
|
assistantMessage?: string;
|
|
5
6
|
}
|
|
6
7
|
|
|
8
|
+
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building messages client-side. */
|
|
7
9
|
export function getFirstMessages(instructions: FirstMessages): any[] {
|
|
8
10
|
const messages = [];
|
|
9
11
|
|
package/src/hooks/UseChatHook.ts
CHANGED
|
@@ -3,38 +3,74 @@ import { Tool } from '@rimori/client';
|
|
|
3
3
|
import { useRimori } from '../providers/PluginProvider';
|
|
4
4
|
import { Message, ToolInvocation } from '@rimori/client';
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export interface UseChatConfig {
|
|
7
|
+
/** Server-side prompt name (e.g. 'storytelling.story'). */
|
|
8
|
+
prompt?: string;
|
|
9
|
+
/** Variables for the server-side prompt template. */
|
|
10
|
+
promptVariables?: Record<string, any>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useChat(
|
|
14
|
+
tools?: Tool[],
|
|
15
|
+
options?: {
|
|
16
|
+
/** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
|
|
17
|
+
knowledgeId?: string;
|
|
18
|
+
/** Server-side prompt name (e.g. 'storytelling.story'). When set, the backend resolves the system prompt. */
|
|
19
|
+
prompt?: string;
|
|
20
|
+
/** Variables for the server-side prompt template. */
|
|
21
|
+
promptVariables?: Record<string, any>;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
7
24
|
const [messages, setMessages] = React.useState<Message[]>([]);
|
|
8
25
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
9
26
|
const { ai } = useRimori();
|
|
27
|
+
const configRef = React.useRef<UseChatConfig>({
|
|
28
|
+
prompt: options?.prompt,
|
|
29
|
+
promptVariables: options?.promptVariables,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Set (or update) the prompt configuration after mount.
|
|
34
|
+
* Call this before the first `append` to configure the server-side prompt
|
|
35
|
+
* without needing refs. Does NOT trigger AI generation.
|
|
36
|
+
*/
|
|
37
|
+
const init = React.useCallback((config: UseChatConfig) => {
|
|
38
|
+
configRef.current = { ...configRef.current, ...config };
|
|
39
|
+
}, []);
|
|
10
40
|
|
|
11
41
|
const append = (appendMessages: Message[]) => {
|
|
12
42
|
const allMessages = [...messages, ...appendMessages];
|
|
13
43
|
setMessages(allMessages);
|
|
14
|
-
|
|
15
|
-
|
|
44
|
+
setIsLoading(true);
|
|
45
|
+
const knowledgeId = options?.knowledgeId;
|
|
46
|
+
const promptVariables = configRef.current.promptVariables ?? options?.promptVariables;
|
|
47
|
+
const prompt = configRef.current.prompt ?? options?.prompt;
|
|
48
|
+
|
|
49
|
+
void ai.getSteamedText(
|
|
16
50
|
allMessages,
|
|
17
51
|
(id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
18
|
-
const lastMessage = messages[messages.length - 1];
|
|
19
52
|
setIsLoading(!finished);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
53
|
+
setMessages((prev) => {
|
|
54
|
+
const last = prev[prev.length - 1];
|
|
55
|
+
if (last?.id === id) {
|
|
56
|
+
return [...prev.slice(0, -1), { ...last, content: message, toolCalls: toolInvocations }];
|
|
57
|
+
}
|
|
58
|
+
return [...prev, { id, role: 'assistant', content: message, toolCalls: toolInvocations }];
|
|
59
|
+
});
|
|
27
60
|
},
|
|
28
61
|
tools,
|
|
29
62
|
false,
|
|
30
63
|
undefined,
|
|
31
64
|
knowledgeId,
|
|
65
|
+
prompt,
|
|
66
|
+
promptVariables,
|
|
32
67
|
);
|
|
33
68
|
};
|
|
34
69
|
|
|
35
70
|
return {
|
|
36
71
|
messages,
|
|
37
72
|
append,
|
|
73
|
+
init,
|
|
38
74
|
isLoading,
|
|
39
75
|
setMessages,
|
|
40
76
|
lastMessage: messages[messages.length - 1] as Message | undefined,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import type { BasePluginSettings, ExplicitUndefined } from '@rimori/client';
|
|
3
|
+
import { useRimori } from '../providers/PluginProvider';
|
|
4
|
+
|
|
5
|
+
export interface UseSettingsResult<T extends BasePluginSettings> {
|
|
6
|
+
/** The current settings (starts as `defaultSettings`, updated once loaded from server). */
|
|
7
|
+
settings: ExplicitUndefined<T>;
|
|
8
|
+
/** True while the initial load is in progress. */
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
/** Persist a partial or full settings update. Merges with current settings. */
|
|
11
|
+
setSettings: (update: Partial<T>) => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hook for managing plugin settings with loading state.
|
|
16
|
+
*
|
|
17
|
+
* Loads settings on mount (merging with defaults via `plugin.getSettings`),
|
|
18
|
+
* exposes the current value, a loading flag, and a setter that persists changes.
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { settings, isLoading, setSettings } = useSettings(DEFAULT_SETTINGS);
|
|
22
|
+
* if (isLoading) return <Spinner />;
|
|
23
|
+
* await setSettings({ genre: 'mystery' });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function useSettings<T extends BasePluginSettings>(defaultSettings: ExplicitUndefined<T>): UseSettingsResult<T> {
|
|
27
|
+
const plugin = useRimori();
|
|
28
|
+
const [settings, setSettingsState] = useState<ExplicitUndefined<T>>(defaultSettings);
|
|
29
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
let cancelled = false;
|
|
33
|
+
void plugin.plugin.getSettings<T>(defaultSettings).then((loaded) => {
|
|
34
|
+
if (cancelled) return;
|
|
35
|
+
setSettingsState(loaded);
|
|
36
|
+
setIsLoading(false);
|
|
37
|
+
});
|
|
38
|
+
return () => {
|
|
39
|
+
cancelled = true;
|
|
40
|
+
};
|
|
41
|
+
// Only run on mount — defaultSettings reference is stable by convention.
|
|
42
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const setSettings = useCallback(
|
|
46
|
+
async (update: Partial<T>): Promise<void> => {
|
|
47
|
+
const merged = { ...settings, ...update } as ExplicitUndefined<T>;
|
|
48
|
+
setSettingsState(merged);
|
|
49
|
+
await plugin.plugin.setSettings(merged);
|
|
50
|
+
},
|
|
51
|
+
[settings, plugin],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return { settings, isLoading, setSettings };
|
|
55
|
+
}
|
package/src/index.ts
CHANGED