Spaces:
Running
Running
File size: 5,279 Bytes
0bb4dfa 4944128 0bb4dfa 4944128 0bb4dfa 1ec996f af5d1df 11bf9b7 d4017c8 11bf9b7 e661e91 0bb4dfa 1ec996f af5d1df 11bf9b7 e661e91 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | /**
* Tiny localStorage shim for the CCAI demo.
*
* Single namespace `ccai-vibe-demo` so we never collide with anything else
* the host page is doing. Schema-versioned so we can migrate if/when the
* shape changes.
*/
const NS = 'ccai-vibe-demo';
const SCHEMA_VERSION = 1;
/** Demo trio — ids/names mirror backend/app/services/extra_personas.py */
export const DEFAULT_DEMO_PERSONAS = [
{
participant_id: 'extra_elena_financial_strategist',
name: 'Elena — Financial Strategist',
},
{
participant_id: 'extra_marcus_technology_strategist',
name: 'Marcus — Technology Strategist',
},
{
participant_id: 'extra_amira_security_advisor',
name: 'Dr. Amira — Security & Privacy Advisor',
},
];
/** Default panel for first load / empty saved selection (demo trio). */
export const DEFAULT_PARTICIPANT_IDS = DEFAULT_DEMO_PERSONAS.map(
(p) => p.participant_id,
);
export const DEFAULT_PARTICIPANTS_ENABLED = Object.fromEntries(
DEFAULT_PARTICIPANT_IDS.map((id) => [id, true]),
);
/** Use saved selection when non-empty; otherwise the demo default trio. */
export function resolveInitialParticipants(persisted) {
const saved = persisted?.participants_selected;
if (Array.isArray(saved) && saved.length > 0) {
return {
selectedIds: saved,
enabledMap: persisted.participants_enabled || {},
};
}
return {
selectedIds: [...DEFAULT_PARTICIPANT_IDS],
enabledMap: { ...DEFAULT_PARTICIPANTS_ENABLED },
};
}
const DEFAULTS = {
schema_version: SCHEMA_VERSION,
expert_personas: [],
participants_selected: [...DEFAULT_PARTICIPANT_IDS],
participants_enabled: { ...DEFAULT_PARTICIPANTS_ENABLED },
model_assignments: {},
orchestrator_model_id: null,
summarizer_model_id: null,
max_participants: 5,
theme: null,
// Sparse map of user overrides to ConversationLimits fields. Empty
// map = "use server defaults". The server clamps anything we send,
// so storing whatever the user typed (including stale values from
// an earlier session) is safe.
conversation_limits: {},
// When true, the participants dropdown shows "Select N Automatically"
// as the active mode and the per-persona checkboxes are hidden.
// The auto-select happens just before /chat/start.
auto_select_mode: false,
// In-the-loop human participant. null when no human is configured.
// Shape when set:
// { participant_id, name, profile_text,
// credential_summary: {
// name, expertise, personality,
// credibility_for_question, bias_to_watch
// } }
// Persisted across page reloads so the user doesn't have to re-author
// their summary every session. Cleared from storage when the user
// removes the human via the sidebar.
human_participant: null,
// Conversation-format plugin choices (see backend
// /api/chat/conversation-formats). null means "use server default"
// — the backend returns `default_structure_id` / `default_decision_id`
// alongside the catalog so the UI can highlight them.
conversation_structure_id: null,
decision_method_id: null,
};
function readAll() {
try {
const raw = window.localStorage.getItem(NS);
if (!raw) return { ...DEFAULTS };
const parsed = JSON.parse(raw);
if (parsed.schema_version !== SCHEMA_VERSION) {
// Future-proofing: migrate or wipe. For v1 we just merge with defaults.
return { ...DEFAULTS, ...parsed, schema_version: SCHEMA_VERSION };
}
return { ...DEFAULTS, ...parsed };
} catch (err) {
console.warn('localStorage read failed:', err);
return { ...DEFAULTS };
}
}
function writeAll(state) {
try {
window.localStorage.setItem(NS, JSON.stringify(state));
} catch (err) {
console.warn('localStorage write failed:', err);
}
}
export function loadState() {
return readAll();
}
export function patchState(patch) {
const current = readAll();
const next = { ...current, ...patch };
writeAll(next);
return next;
}
export function setExpertPersonas(list) {
return patchState({ expert_personas: list });
}
export function setParticipantsSelected(ids) {
return patchState({ participants_selected: ids });
}
export function setParticipantsEnabled(map) {
return patchState({ participants_enabled: map });
}
export function setModelAssignments(map) {
return patchState({ model_assignments: map });
}
export function setOrchestratorModelId(modelId) {
return patchState({ orchestrator_model_id: modelId });
}
export function setSummarizerModelId(modelId) {
return patchState({ summarizer_model_id: modelId });
}
export function setMaxParticipants(n) {
return patchState({ max_participants: n });
}
export function setTheme(theme) {
return patchState({ theme });
}
export function setConversationLimits(limitsMap) {
return patchState({ conversation_limits: limitsMap || {} });
}
export function setAutoSelectMode(on) {
return patchState({ auto_select_mode: !!on });
}
export function setHumanParticipant(humanOrNull) {
return patchState({ human_participant: humanOrNull || null });
}
export function setConversationStructureId(id) {
return patchState({ conversation_structure_id: id || null });
}
export function setDecisionMethodId(id) {
return patchState({ decision_method_id: id || null });
}
|