@scoopika/react
Version:
Build interactive AI-powered React applications around AI agents
351 lines (345 loc) • 11.4 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
useChatState: () => useChatState,
useVoiceChatState: () => useVoiceChatState
});
module.exports = __toCommonJS(src_exports);
// src/state.ts
var import_client = require("@scoopika/client");
var import_react = require("react");
// src/utils/sleep.ts
function sleep(ms) {
if (typeof ms !== "number") {
ms = 0;
}
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
var sleep_default = sleep;
// src/state.ts
var setupRequest = (session_id, inputs, run_id, user_id) => {
const req = {
role: "user",
at: Date.now(),
session_id,
run_id: run_id || crypto.randomUUID(),
user_id,
request: inputs,
resolved_message: "PLACEHOLDER"
};
return req;
};
var agentPlaceholder = ({
session_id,
run_id,
audio,
tool_calls,
content
}) => {
const placeholder = {
role: "model",
at: Date.now(),
session_id,
run_id,
response: {
run_id,
session_id,
audio,
tool_calls,
content
}
};
return placeholder;
};
var sortedMessages = (messages) => messages.sort((a, b) => a.at - b.at);
function useChatState(agent, state_options) {
if (typeof agent === "string") {
agent = new import_client.AgentClient(agent);
}
const [agentInstance] = (0, import_react.useState)(agent);
const [session, setSession] = (0, import_react.useState)(
(state_options == null ? void 0 : state_options.session_id) ?? "session_" + crypto.randomUUID()
);
const [status, setStatus] = (0, import_react.useState)();
const [generating, setGenerating] = (0, import_react.useState)(false);
const [loading, setLoading] = (0, import_react.useState)(false);
const [messages, setMessages] = (0, import_react.useState)((state_options == null ? void 0 : state_options.messages) || []);
const [streamPlaceholder, setStreamPlaceholder] = (0, import_react.useState)(void 0);
const newRequest = async ({
inputs = {},
options,
hooks
} = {}) => {
try {
while (generating || loading) {
await sleep_default(5);
}
setLoading(true);
options = { ...options || {} };
options.session_id = session;
const request = setupRequest(session, inputs, options == null ? void 0 : options.run_id);
let run_id = request.run_id;
setStreamPlaceholder(
agentPlaceholder({
session_id: session,
run_id,
audio: [],
content: "",
tool_calls: []
})
);
setMessages((prev) => [...sortedMessages(prev), request]);
setStatus("Thinking...");
const all_hooks = {
...hooks || {},
onStart: (info) => {
run_id = info.run_id;
if (state_options == null ? void 0 : state_options.scroll) state_options.scroll();
},
onToken: (token) => {
if (loading) setLoading(false);
if (!generating) setGenerating(true);
if (status) setStatus(void 0);
setStreamPlaceholder((prev) => {
if (!prev) return;
return {
...prev,
response: {
...prev.response,
content: prev.response.content + token
}
};
});
if (hooks == null ? void 0 : hooks.onToken) hooks.onToken(token);
if (state_options == null ? void 0 : state_options.scroll) state_options.scroll();
},
onToolCall: (call) => {
setStatus(`Talking with ${call.function.name}`);
if (hooks == null ? void 0 : hooks.onToolCall) hooks.onToolCall(call);
},
onToolResult: (res) => {
setStatus(void 0);
setStreamPlaceholder((prev) => {
if (!prev) return;
return {
...prev,
response: {
...prev.response,
tools_calls: [...prev.response.tool_calls, res]
}
};
});
if (hooks == null ? void 0 : hooks.onToolResult) hooks.onToolResult(res);
},
onModelResponse: async (response2) => {
setStatus(void 0);
setStreamPlaceholder(void 0);
if (response2.error === null) {
setMessages((prev) => sortedMessages(
[...prev, agentPlaceholder(response2.data)]
));
}
if (hooks == null ? void 0 : hooks.onModelResponse) hooks.onModelResponse(response2);
}
};
const response = await agentInstance.run({
inputs,
options,
hooks: all_hooks
});
return response;
} catch (err) {
const err_string = typeof err === "string" ? err : typeof (err == null ? void 0 : err.msg) === "string" ? err.msg : JSON.stringify(err);
return { data: null, error: err_string };
} finally {
setStatus(void 0);
setLoading(false);
setGenerating(false);
if (state_options == null ? void 0 : state_options.scroll) state_options.scroll();
}
};
return {
generating,
loading,
status,
streamPlaceholder,
messages,
setMessages,
newRequest,
agent,
session,
setSession
};
}
// src/voice_state.ts
var import_client2 = require("@scoopika/client");
var import_react2 = require("react");
function useVoiceChatState(agent, state_options = {}) {
const chatState = useChatState(agent, state_options);
const [agentVoicePlayer, setAgentVoicePlayer] = (0, import_react2.useState)(null);
const [voiceRecorder, setVoiceRecorder] = (0, import_react2.useState)(
null
);
const [voicePlaying, setVoicePlaying] = (0, import_react2.useState)(false);
const [visualizer, setVisualizer] = (0, import_react2.useState)(null);
const [recorderState, setRecorderState] = (0, import_react2.useState)("stopped");
const [agentVoicePaused, setPlayerPaused] = (0, import_react2.useState)(false);
const [working, setWorking] = (0, import_react2.useState)(false);
const [recognizedText, setRecognizedText] = (0, import_react2.useState)();
const [supportRecognition, setSupportRecognition] = (0, import_react2.useState)(
null
);
const pauseAgentVoice = () => {
if (!agentVoicePlayer) return;
agentVoicePlayer.pause();
setPlayerPaused(true);
};
const resumeAgentVoice = () => {
if (!agentVoicePlayer) return;
agentVoicePlayer.resume();
setPlayerPaused(false);
};
const updateRecognizedText = (text) => {
if (voiceRecorder) voiceRecorder.stop();
if (voiceRecorder) voiceRecorder.text = text;
setRecognizedText(text);
};
(0, import_react2.useEffect)(() => {
setVoiceRecorder(
new import_client2.VoiceRecorder({
onStateChange: (state) => setRecorderState(state),
onText: (text) => setRecognizedText(text),
allowInBrowserRecognition: state_options == null ? void 0 : state_options.allowInBrowserSpeechRecognition
})
);
if (voiceRecorder) {
setSupportRecognition(voiceRecorder.recognition !== null);
}
const visualize = state_options == null ? void 0 : state_options.agent_voice;
if (visualize == null ? void 0 : visualize.canvas) {
setVisualizer(
new import_client2.VoiceVisualizer(
visualize.audio,
visualize.canvas,
visualize.wave_color
)
);
}
}, []);
const newRequest = async ({
inputs,
options,
hooks
} = {}) => {
try {
if (agentVoicePlayer == null ? void 0 : agentVoicePlayer.started) {
agentVoicePlayer.pause();
setVoicePlaying(false);
setPlayerPaused(false);
}
let player = null;
if (voiceRecorder == null ? void 0 : voiceRecorder.started) voiceRecorder.stop();
inputs = inputs || {};
if (voiceRecorder == null ? void 0 : voiceRecorder.started) {
const recorderInputs = voiceRecorder ? await voiceRecorder.asRunInput() : null;
let message = (inputs == null ? void 0 : inputs.message) || "";
const audio = (inputs == null ? void 0 : inputs.audio) || [];
if (recorderInputs == null ? void 0 : recorderInputs.message) {
if (message.length > 0) message += "\n";
message += recorderInputs.message;
}
if (recorderInputs == null ? void 0 : recorderInputs.audio) {
audio.push(...recorderInputs.audio);
}
inputs = { ...inputs || {}, message, audio };
}
setWorking(true);
if (voiceRecorder == null ? void 0 : voiceRecorder.started) await voiceRecorder.finish();
options = { voice: true, ...options || {} };
if (state_options == null ? void 0 : state_options.agent_voice) {
player = new import_client2.RunVoicePlayer(state_options.agent_voice.audio);
setAgentVoicePlayer(player);
}
if (visualizer) visualizer.getReady();
const all_hooks = {
...hooks || {},
onAudio: (stream) => {
if ((state_options == null ? void 0 : state_options.auto_play_audio) !== false) {
if (!(player == null ? void 0 : player.paused)) {
setPlayerPaused(false);
}
setVoicePlaying(true);
if (player) player.queue(stream);
}
if (hooks == null ? void 0 : hooks.onAudio) hooks.onAudio(stream);
}
};
const response = await chatState.newRequest({
inputs,
options,
hooks: all_hooks
});
setWorking(false);
if (player && (response == null ? void 0 : response.error) === null)
await player.finish(response.data.audio.length);
setVoicePlaying(false);
return response;
} catch (err) {
const err_string = typeof err === "string" ? err : typeof (err == null ? void 0 : err.msg) === "string" ? err.msg : JSON.stringify(err);
return { data: null, error: err_string };
} finally {
setWorking(false);
setVoicePlaying(false);
}
};
return {
...chatState,
voiceRecorder,
setVoiceRecorder,
voicePlaying,
setVoicePlaying,
agentVoicePlayer,
setAgentVoicePlayer,
newRequest,
visualizer,
setVisualizer,
recorderState,
setRecorderState,
recognizedText,
setRecognizedText,
supportRecognition,
setSupportRecognition,
updateRecognizedText,
working,
setWorking,
pauseAgentVoice,
resumeAgentVoice,
agentVoicePaused,
setPlayerPaused
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
useChatState,
useVoiceChatState
});