mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-10-01 05:54:56 +02:00
[hAIk]: integrate modes backend-bridge into frontend: add Mode type to bridge with proper typing, rewrite modesSlice to import thunks from backend-bridge (matching dashboards pattern), update useModes hook to use LIST_MODES/CREATE_MODE/UPDATE_MODE/RESET_MODE/DELETE_MODE, update useAgentChat/ModelModeSelector/useSettings to use LIST_MODES, remove legacy @/shared/config dependency from modes
This commit is contained in:
@@ -20,7 +20,7 @@ import AttachFileIcon from '@mui/icons-material/AttachFile';
|
||||
import AdsClickIcon from '@mui/icons-material/AdsClick';
|
||||
import { useElementSelection } from '@/app/components/ElementSelectionContext';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { fetchModes } from '@/shared/state/modesSlice';
|
||||
import { LIST_MODES } from '@/shared/state/modesSlice';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import ContextRing from './ContextRing';
|
||||
|
||||
@@ -69,7 +69,7 @@ const ModelModeSelector: React.FC<Props> = ({
|
||||
const [modeAnchor, setModeAnchor] = useState<HTMLElement | null>(null);
|
||||
const [modelAnchor, setModelAnchor] = useState<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => { if (modesArr.length === 0) dispatch(fetchModes()); }, [dispatch, modesArr.length]);
|
||||
useEffect(() => { if (modesArr.length === 0) dispatch(LIST_MODES()); }, [dispatch, modesArr.length]);
|
||||
|
||||
const allModelOptions = useMemo(() => {
|
||||
if (!modelsLoaded || Object.keys(modelsByProvider).length === 0) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
META_LAUNCH_AND_SEND
|
||||
} from '@/shared/backend-bridge/apps/agents';
|
||||
import { updateSessionMode, updateSessionModel } from '@/shared/state/agentsSlice';
|
||||
import { fetchModes } from '@/shared/state/modesSlice';
|
||||
import { LIST_MODES } from '@/shared/state/modesSlice';
|
||||
import { setGlowingBrowserCards, fadeGlowingBrowserCards, clearGlowingBrowserCards } from '@/shared/state/dashboardLayoutSlice';
|
||||
|
||||
export interface QueuedMessage {
|
||||
@@ -48,7 +48,7 @@ export function useAgentChat({ sessionId: sessionIdProp }: UseAgentChatParams) {
|
||||
dispatch(GET_SESSION(id));
|
||||
}, [id, isDraft, dispatch]);
|
||||
|
||||
useEffect(() => { if (Object.keys(modesMap).length === 0) dispatch(fetchModes()); }, [dispatch, modesMap]);
|
||||
useEffect(() => { if (Object.keys(modesMap).length === 0) dispatch(LIST_MODES()); }, [dispatch, modesMap]);
|
||||
|
||||
const sessionSystemPrompt = session?.system_prompt;
|
||||
const sessionTargetDirectory = session?.target_directory;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import {
|
||||
fetchModes,
|
||||
createMode,
|
||||
updateMode,
|
||||
deleteMode,
|
||||
resetMode,
|
||||
LIST_MODES,
|
||||
CREATE_MODE,
|
||||
UPDATE_MODE,
|
||||
DELETE_MODE,
|
||||
RESET_MODE,
|
||||
Mode,
|
||||
} from '@/shared/state/modesSlice';
|
||||
import { fetchBuiltinTools, fetchTools } from '@/shared/state/toolsSlice';
|
||||
@@ -30,7 +30,7 @@ export function useModes() {
|
||||
const [browseOpen, setBrowseOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchModes());
|
||||
dispatch(LIST_MODES());
|
||||
dispatch(fetchBuiltinTools());
|
||||
dispatch(fetchTools());
|
||||
dispatch(fetchSkills());
|
||||
@@ -71,15 +71,15 @@ export function useModes() {
|
||||
};
|
||||
|
||||
if (editingId) {
|
||||
await dispatch(updateMode({ id: editingId, ...payload }));
|
||||
await dispatch(UPDATE_MODE({ modeId: editingId, ...payload }));
|
||||
} else {
|
||||
await dispatch(createMode(payload as any));
|
||||
await dispatch(CREATE_MODE(payload as any));
|
||||
}
|
||||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
await dispatch(deleteMode(id));
|
||||
await dispatch(DELETE_MODE(id));
|
||||
};
|
||||
|
||||
const editingIsBuiltin = editingId ? items[editingId]?.is_builtin ?? false : false;
|
||||
@@ -104,8 +104,8 @@ export function useModes() {
|
||||
|
||||
const handleReset = async () => {
|
||||
if (!editingId) return;
|
||||
const action = await dispatch(resetMode(editingId));
|
||||
if (resetMode.fulfilled.match(action)) {
|
||||
const action = await dispatch(RESET_MODE(editingId));
|
||||
if (RESET_MODE.fulfilled.match(action)) {
|
||||
const m = action.payload;
|
||||
setForm({
|
||||
name: m.name,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { updateSettings, closeSettingsModal, AppSettings } from '@/shared/state/settingsSlice';
|
||||
import { fetchModels } from '@/shared/state/modelsSlice';
|
||||
import { setChecking, setUpdateError } from '@/shared/state/updateSlice';
|
||||
import { fetchModes } from '@/shared/state/modesSlice';
|
||||
import { LIST_MODES } from '@/shared/state/modesSlice';
|
||||
import { useClaudeTokens, useThemeMode } from '@/shared/styles/ThemeContext';
|
||||
|
||||
export function useSettings() {
|
||||
@@ -28,7 +28,7 @@ export function useSettings() {
|
||||
const [recordingShortcut, setRecordingShortcut] = useState(false);
|
||||
const [confirmDiscard, setConfirmDiscard] = useState(false);
|
||||
const [showApiHelp, setShowApiHelp] = useState(false);
|
||||
useEffect(() => { dispatch(fetchModes()); }, [dispatch]);
|
||||
useEffect(() => { dispatch(LIST_MODES()); }, [dispatch]);
|
||||
useEffect(() => { if (open) setActiveTab('general'); }, [open]);
|
||||
useEffect(() => { if (loaded) setForm({ ...settings }); }, [loaded, settings]);
|
||||
const hasChanges = JSON.stringify(form) !== JSON.stringify(settings);
|
||||
|
||||
@@ -3,19 +3,32 @@ import { API_BASE } from '@/shared/backend-bridge/base_routes';
|
||||
|
||||
const MODES_API: string = `${API_BASE}/modes`;
|
||||
|
||||
export interface Mode {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
system_prompt: string | null;
|
||||
tools: string[] | null;
|
||||
default_next_mode: string | null;
|
||||
is_builtin: boolean;
|
||||
icon: string;
|
||||
color: string;
|
||||
default_folder: string | null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mode CRUD
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
const list_modes_endpoint: string = `${MODES_API}/list`;
|
||||
async function list_modes_function(): Promise<{ modes: Record<string, unknown>[]; builtin_defaults: Record<string, Record<string, unknown>> }> {
|
||||
async function list_modes_function(): Promise<{ modes: Mode[]; builtin_defaults: Record<string, Mode> }> {
|
||||
const res = await fetch(list_modes_endpoint, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as { modes: Record<string, unknown>[]; builtin_defaults: Record<string, Record<string, unknown>> };
|
||||
return data as { modes: Mode[]; builtin_defaults: Record<string, Mode> };
|
||||
}
|
||||
export const LIST_MODES = createAsyncThunk(
|
||||
list_modes_endpoint,
|
||||
@@ -24,13 +37,13 @@ export const LIST_MODES = createAsyncThunk(
|
||||
|
||||
|
||||
const get_mode_endpoint: string = `${MODES_API}/get`;
|
||||
async function get_mode_function(modeId: string): Promise<Record<string, unknown>> {
|
||||
async function get_mode_function(modeId: string): Promise<Mode> {
|
||||
const res = await fetch(`${MODES_API}/${modeId}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as Record<string, unknown>;
|
||||
return data as Mode;
|
||||
}
|
||||
export const GET_MODE = createAsyncThunk(
|
||||
get_mode_endpoint,
|
||||
@@ -48,14 +61,14 @@ async function create_mode_function(body: {
|
||||
icon?: string;
|
||||
color?: string;
|
||||
default_folder?: string | null;
|
||||
}): Promise<{ ok: boolean; mode: Record<string, unknown> }> {
|
||||
}): Promise<Mode> {
|
||||
const res = await fetch(create_mode_endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as { ok: boolean; mode: Record<string, unknown> };
|
||||
return data.mode as Mode;
|
||||
}
|
||||
export const CREATE_MODE = createAsyncThunk(
|
||||
create_mode_endpoint,
|
||||
@@ -74,7 +87,7 @@ async function update_mode_function(args: {
|
||||
icon?: string;
|
||||
color?: string;
|
||||
default_folder?: string | null;
|
||||
}): Promise<{ ok: boolean; mode: Record<string, unknown> }> {
|
||||
}): Promise<Mode> {
|
||||
const { modeId, ...updates } = args;
|
||||
const res = await fetch(`${MODES_API}/${modeId}`, {
|
||||
method: 'PUT',
|
||||
@@ -82,7 +95,7 @@ async function update_mode_function(args: {
|
||||
body: JSON.stringify(updates),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as { ok: boolean; mode: Record<string, unknown> };
|
||||
return data.mode as Mode;
|
||||
}
|
||||
export const UPDATE_MODE = createAsyncThunk(
|
||||
update_mode_endpoint,
|
||||
@@ -91,13 +104,13 @@ export const UPDATE_MODE = createAsyncThunk(
|
||||
|
||||
|
||||
const reset_mode_endpoint: string = `${MODES_API}/reset`;
|
||||
async function reset_mode_function(modeId: string): Promise<{ ok: boolean; mode: Record<string, unknown> }> {
|
||||
async function reset_mode_function(modeId: string): Promise<Mode> {
|
||||
const res = await fetch(`${MODES_API}/${modeId}/reset`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as { ok: boolean; mode: Record<string, unknown> };
|
||||
return data.mode as Mode;
|
||||
}
|
||||
export const RESET_MODE = createAsyncThunk(
|
||||
reset_mode_endpoint,
|
||||
@@ -106,13 +119,12 @@ export const RESET_MODE = createAsyncThunk(
|
||||
|
||||
|
||||
const delete_mode_endpoint: string = `${MODES_API}/delete`;
|
||||
async function delete_mode_function(modeId: string): Promise<{ ok: boolean }> {
|
||||
const res = await fetch(`${MODES_API}/${modeId}`, {
|
||||
async function delete_mode_function(modeId: string): Promise<string> {
|
||||
await fetch(`${MODES_API}/${modeId}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as { ok: boolean };
|
||||
return modeId;
|
||||
}
|
||||
export const DELETE_MODE = createAsyncThunk(
|
||||
delete_mode_endpoint,
|
||||
@@ -121,14 +133,14 @@ export const DELETE_MODE = createAsyncThunk(
|
||||
|
||||
|
||||
const get_mode_by_id_endpoint: string = `${MODES_API}/get_mode_by_id`;
|
||||
async function get_mode_by_id_function(modeId: string): Promise<Record<string, unknown> | null> {
|
||||
async function get_mode_by_id_function(modeId: string): Promise<Mode | null> {
|
||||
const res = await fetch(get_mode_by_id_endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mode_id: modeId }),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data as Record<string, unknown> | null;
|
||||
return data as Mode | null;
|
||||
}
|
||||
export const GET_MODE_BY_ID = createAsyncThunk(
|
||||
get_mode_by_id_endpoint,
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { API_BASE } from '@/shared/config';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import {
|
||||
LIST_MODES,
|
||||
CREATE_MODE,
|
||||
UPDATE_MODE,
|
||||
RESET_MODE,
|
||||
DELETE_MODE,
|
||||
} from '@/shared/backend-bridge/apps/modes';
|
||||
import type { Mode } from '@/shared/backend-bridge/apps/modes';
|
||||
|
||||
const MODES_API = `${API_BASE}/modes`;
|
||||
|
||||
export interface Mode {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
system_prompt: string | null;
|
||||
tools: string[] | null;
|
||||
default_next_mode: string | null;
|
||||
is_builtin: boolean;
|
||||
icon: string;
|
||||
color: string;
|
||||
default_folder: string | null;
|
||||
}
|
||||
export type { Mode };
|
||||
export { LIST_MODES, CREATE_MODE, UPDATE_MODE, RESET_MODE, DELETE_MODE };
|
||||
|
||||
interface ModesState {
|
||||
items: Record<string, Mode>;
|
||||
@@ -25,75 +20,25 @@ interface ModesState {
|
||||
|
||||
const initialState: ModesState = { items: {}, builtinDefaults: {}, loading: false, loaded: false };
|
||||
|
||||
export const fetchModes = createAsyncThunk(
|
||||
'modes/fetch',
|
||||
async () => {
|
||||
const res = await fetch(`${MODES_API}/list`);
|
||||
const data = await res.json();
|
||||
return { modes: data.modes as Mode[], builtinDefaults: (data.builtin_defaults ?? {}) as Record<string, Mode> };
|
||||
},
|
||||
{ condition: (_, { getState }) => !(getState() as { modes: ModesState }).modes.loading },
|
||||
);
|
||||
|
||||
export const createMode = createAsyncThunk(
|
||||
'modes/create',
|
||||
async (body: Omit<Mode, 'id' | 'is_builtin'>) => {
|
||||
const res = await fetch(`${MODES_API}/create`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data.mode as Mode;
|
||||
}
|
||||
);
|
||||
|
||||
export const updateMode = createAsyncThunk(
|
||||
'modes/update',
|
||||
async ({ id, ...updates }: Partial<Mode> & { id: string }) => {
|
||||
const res = await fetch(`${MODES_API}/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updates),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data.mode as Mode;
|
||||
}
|
||||
);
|
||||
|
||||
export const resetMode = createAsyncThunk(
|
||||
'modes/reset',
|
||||
async (id: string) => {
|
||||
const res = await fetch(`${MODES_API}/${id}/reset`, { method: 'POST' });
|
||||
const data = await res.json();
|
||||
return data.mode as Mode;
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteMode = createAsyncThunk('modes/delete', async (id: string) => {
|
||||
await fetch(`${MODES_API}/${id}`, { method: 'DELETE' });
|
||||
return id;
|
||||
});
|
||||
|
||||
const modesSlice = createSlice({
|
||||
name: 'modes',
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(fetchModes.pending, (state) => { state.loading = true; })
|
||||
.addCase(fetchModes.fulfilled, (state, action) => {
|
||||
.addCase(LIST_MODES.pending, (state) => { state.loading = true; })
|
||||
.addCase(LIST_MODES.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.loaded = true;
|
||||
state.items = {};
|
||||
for (const m of action.payload.modes) state.items[m.id] = m;
|
||||
state.builtinDefaults = action.payload.builtinDefaults;
|
||||
state.builtinDefaults = action.payload.builtin_defaults;
|
||||
})
|
||||
.addCase(fetchModes.rejected, (state) => { state.loading = false; state.loaded = true; })
|
||||
.addCase(createMode.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(updateMode.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(resetMode.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(deleteMode.fulfilled, (state, action) => { delete state.items[action.payload]; });
|
||||
.addCase(LIST_MODES.rejected, (state) => { state.loading = false; state.loaded = true; })
|
||||
.addCase(CREATE_MODE.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(UPDATE_MODE.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(RESET_MODE.fulfilled, (state, action) => { state.items[action.payload.id] = action.payload; })
|
||||
.addCase(DELETE_MODE.fulfilled, (state, action) => { delete state.items[action.payload]; });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user