Skip to content

Commit bec8181

Browse files
committed
add support for squads and simulations
1 parent 85ee548 commit bec8181

8 files changed

Lines changed: 728 additions & 69 deletions

File tree

src/apply.ts

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,116 @@ export async function applyAssistant(
111111
}
112112
}
113113

114+
export async function applySquad(
115+
resource: ResourceFile,
116+
state: StateFile
117+
): Promise<string> {
118+
const { resourceId, data } = resource;
119+
const existingUuid = state.squads[resourceId];
120+
121+
// Resolve assistant references in members
122+
const payload = resolveReferences(data as Record<string, unknown>, state);
123+
124+
if (existingUuid) {
125+
const updatePayload = removeExcludedKeys(payload, "squads");
126+
console.log(` 🔄 Updating squad: ${resourceId} (${existingUuid})`);
127+
await vapiRequest("PATCH", `/squad/${existingUuid}`, updatePayload);
128+
return existingUuid;
129+
} else {
130+
console.log(` ✨ Creating squad: ${resourceId}`);
131+
const result = await vapiRequest("POST", "/squad", payload);
132+
return result.id;
133+
}
134+
}
135+
136+
export async function applyPersonality(
137+
resource: ResourceFile,
138+
state: StateFile
139+
): Promise<string> {
140+
const { resourceId, data } = resource;
141+
const existingUuid = state.personalities[resourceId];
142+
143+
// Personalities contain inline assistant config, no external references to resolve
144+
const payload = data as Record<string, unknown>;
145+
146+
if (existingUuid) {
147+
const updatePayload = removeExcludedKeys(payload, "personalities");
148+
console.log(` 🔄 Updating personality: ${resourceId} (${existingUuid})`);
149+
await vapiRequest("PATCH", `/eval/simulation/personality/${existingUuid}`, updatePayload);
150+
return existingUuid;
151+
} else {
152+
console.log(` ✨ Creating personality: ${resourceId}`);
153+
const result = await vapiRequest("POST", "/eval/simulation/personality", payload);
154+
return result.id;
155+
}
156+
}
157+
158+
export async function applyScenario(
159+
resource: ResourceFile,
160+
state: StateFile
161+
): Promise<string> {
162+
const { resourceId, data } = resource;
163+
const existingUuid = state.scenarios[resourceId];
164+
165+
// Scenarios have no external references to resolve
166+
const payload = data as Record<string, unknown>;
167+
168+
if (existingUuid) {
169+
const updatePayload = removeExcludedKeys(payload, "scenarios");
170+
console.log(` 🔄 Updating scenario: ${resourceId} (${existingUuid})`);
171+
await vapiRequest("PATCH", `/eval/simulation/scenario/${existingUuid}`, updatePayload);
172+
return existingUuid;
173+
} else {
174+
console.log(` ✨ Creating scenario: ${resourceId}`);
175+
const result = await vapiRequest("POST", "/eval/simulation/scenario", payload);
176+
return result.id;
177+
}
178+
}
179+
180+
export async function applySimulation(
181+
resource: ResourceFile,
182+
state: StateFile
183+
): Promise<string> {
184+
const { resourceId, data } = resource;
185+
const existingUuid = state.simulations[resourceId];
186+
187+
// Resolve personality and scenario references
188+
const payload = resolveReferences(data as Record<string, unknown>, state);
189+
190+
if (existingUuid) {
191+
const updatePayload = removeExcludedKeys(payload, "simulations");
192+
console.log(` 🔄 Updating simulation: ${resourceId} (${existingUuid})`);
193+
await vapiRequest("PATCH", `/eval/simulation/${existingUuid}`, updatePayload);
194+
return existingUuid;
195+
} else {
196+
console.log(` ✨ Creating simulation: ${resourceId}`);
197+
const result = await vapiRequest("POST", "/eval/simulation", payload);
198+
return result.id;
199+
}
200+
}
201+
202+
export async function applySimulationSuite(
203+
resource: ResourceFile,
204+
state: StateFile
205+
): Promise<string> {
206+
const { resourceId, data } = resource;
207+
const existingUuid = state.simulationSuites[resourceId];
208+
209+
// Resolve simulation references
210+
const payload = resolveReferences(data as Record<string, unknown>, state);
211+
212+
if (existingUuid) {
213+
const updatePayload = removeExcludedKeys(payload, "simulationSuites");
214+
console.log(` 🔄 Updating simulation suite: ${resourceId} (${existingUuid})`);
215+
await vapiRequest("PATCH", `/eval/simulation/suite/${existingUuid}`, updatePayload);
216+
return existingUuid;
217+
} else {
218+
console.log(` ✨ Creating simulation suite: ${resourceId}`);
219+
const result = await vapiRequest("POST", "/eval/simulation/suite", payload);
220+
return result.id;
221+
}
222+
}
223+
114224
// ─────────────────────────────────────────────────────────────────────────────
115225
// Post-Apply: Update Tools with Assistant References (for handoff tools)
116226
// ─────────────────────────────────────────────────────────────────────────────
@@ -201,12 +311,27 @@ async function main(): Promise<void> {
201311
const tools = await loadResources<Record<string, unknown>>("tools");
202312
const structuredOutputs = await loadResources<Record<string, unknown>>("structuredOutputs");
203313
const assistants = await loadResources<Record<string, unknown>>("assistants");
314+
const squads = await loadResources<Record<string, unknown>>("squads");
315+
const personalities = await loadResources<Record<string, unknown>>("personalities");
316+
const scenarios = await loadResources<Record<string, unknown>>("scenarios");
317+
const simulations = await loadResources<Record<string, unknown>>("simulations");
318+
const simulationSuites = await loadResources<Record<string, unknown>>("simulationSuites");
204319

205320
// Delete orphaned resources first (checks for orphan references, then deletes)
206321
console.log("\n🗑️ Checking for deleted resources...\n");
207-
await deleteOrphanedResources({ tools, structuredOutputs, assistants }, state);
322+
await deleteOrphanedResources({
323+
tools, structuredOutputs, assistants, squads,
324+
personalities, scenarios, simulations, simulationSuites
325+
}, state);
326+
327+
// Apply in dependency order:
328+
// 1. Base resources (tools, structuredOutputs)
329+
// 2. Assistants (references tools, structuredOutputs)
330+
// 3. Squads (references assistants)
331+
// 4. Simulation building blocks (personalities, scenarios)
332+
// 5. Simulations (references personalities, scenarios)
333+
// 6. Simulation suites (references simulations)
208334

209-
// Apply in dependency order: tools → structured outputs → assistants
210335
console.log("\n🔧 Applying tools...\n");
211336
for (const tool of tools) {
212337
try {
@@ -246,6 +371,61 @@ async function main(): Promise<void> {
246371
}
247372
}
248373

374+
console.log("\n👥 Applying squads...\n");
375+
for (const squad of squads) {
376+
try {
377+
const uuid = await applySquad(squad, state);
378+
state.squads[squad.resourceId] = uuid;
379+
} catch (error) {
380+
console.error(` ❌ Failed to apply squad ${squad.resourceId}:`, error);
381+
throw error;
382+
}
383+
}
384+
385+
console.log("\n🎭 Applying personalities...\n");
386+
for (const personality of personalities) {
387+
try {
388+
const uuid = await applyPersonality(personality, state);
389+
state.personalities[personality.resourceId] = uuid;
390+
} catch (error) {
391+
console.error(` ❌ Failed to apply personality ${personality.resourceId}:`, error);
392+
throw error;
393+
}
394+
}
395+
396+
console.log("\n📋 Applying scenarios...\n");
397+
for (const scenario of scenarios) {
398+
try {
399+
const uuid = await applyScenario(scenario, state);
400+
state.scenarios[scenario.resourceId] = uuid;
401+
} catch (error) {
402+
console.error(` ❌ Failed to apply scenario ${scenario.resourceId}:`, error);
403+
throw error;
404+
}
405+
}
406+
407+
console.log("\n🧪 Applying simulations...\n");
408+
for (const simulation of simulations) {
409+
try {
410+
const uuid = await applySimulation(simulation, state);
411+
state.simulations[simulation.resourceId] = uuid;
412+
} catch (error) {
413+
console.error(` ❌ Failed to apply simulation ${simulation.resourceId}:`, error);
414+
throw error;
415+
}
416+
}
417+
418+
console.log("\n📦 Applying simulation suites...\n");
419+
for (const suite of simulationSuites) {
420+
try {
421+
const uuid = await applySimulationSuite(suite, state);
422+
state.simulationSuites[suite.resourceId] = uuid;
423+
} catch (error) {
424+
console.error(` ❌ Failed to apply simulation suite ${suite.resourceId}:`, error);
425+
throw error;
426+
}
427+
}
428+
249429
// Second pass: Link resources to assistants (now that assistants exist)
250430
console.log("\n🔗 Linking tools to assistant destinations...\n");
251431
await updateToolAssistantRefs(tools, state);
@@ -265,6 +445,11 @@ async function main(): Promise<void> {
265445
console.log(` Tools: ${Object.keys(state.tools).length}`);
266446
console.log(` Structured Outputs: ${Object.keys(state.structuredOutputs).length}`);
267447
console.log(` Assistants: ${Object.keys(state.assistants).length}`);
448+
console.log(` Squads: ${Object.keys(state.squads).length}`);
449+
console.log(` Personalities: ${Object.keys(state.personalities).length}`);
450+
console.log(` Scenarios: ${Object.keys(state.scenarios).length}`);
451+
console.log(` Simulations: ${Object.keys(state.simulations).length}`);
452+
console.log(` Simulation Suites: ${Object.keys(state.simulationSuites).length}`);
268453
}
269454

270455
// Run the apply engine

src/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ export const UPDATE_EXCLUDED_KEYS: Record<ResourceType, string[]> = {
103103
tools: ["type"],
104104
assistants: [],
105105
structuredOutputs: ["type"],
106+
squads: [],
107+
personalities: [],
108+
scenarios: [],
109+
simulations: [],
110+
simulationSuites: [],
106111
};
107112

108113
export function removeExcludedKeys(

0 commit comments

Comments
 (0)