mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
fix(desktop): allow restarting saved relay-mesh agents from the UI (#956)
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -39,23 +39,33 @@ function agent(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
test("relay-mesh agents cannot be manually started without a fresh target", async () => {
|
||||
let called = false;
|
||||
await assert.rejects(
|
||||
startManagedAgentWithRules({
|
||||
agent: agent({
|
||||
test("relay-mesh agents delegate start to the backend preflight", async () => {
|
||||
const meshAgent = agent({
|
||||
envVars: {
|
||||
SPROUT_AGENT_PROVIDER: "openai",
|
||||
OPENAI_COMPAT_BASE_URL: "http://127.0.0.1:9337/v1/",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
let calledWith = null;
|
||||
await startManagedAgentWithRules({
|
||||
agent: meshAgent,
|
||||
startManagedAgent: async (pubkey) => {
|
||||
calledWith = pubkey;
|
||||
},
|
||||
});
|
||||
assert.equal(calledWith, meshAgent.pubkey);
|
||||
|
||||
// Backend preflight failures (e.g. no live serve target) propagate as-is.
|
||||
await assert.rejects(
|
||||
startManagedAgentWithRules({
|
||||
agent: meshAgent,
|
||||
startManagedAgent: async () => {
|
||||
called = true;
|
||||
throw new Error("no live serve target is available for this model");
|
||||
},
|
||||
}),
|
||||
/Relay-mesh agents need a fresh serve target/,
|
||||
/no live serve target/,
|
||||
);
|
||||
assert.equal(called, false);
|
||||
});
|
||||
|
||||
test("ordinary local agents still start normally", async () => {
|
||||
|
||||
@@ -75,18 +75,6 @@ export function resolveManagedAgentChannelId(
|
||||
return matches.length === 1 ? matches[0].id : null;
|
||||
}
|
||||
|
||||
function relayMeshAgentError(agent: ManagedAgent): string | null {
|
||||
if (agent.backend.type !== "local") return null;
|
||||
if (agent.envVars.SPROUT_AGENT_PROVIDER !== "openai") return null;
|
||||
if (
|
||||
agent.envVars.OPENAI_COMPAT_BASE_URL?.replace(/\/+$/, "") !==
|
||||
"http://127.0.0.1:9337/v1"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return "Relay-mesh agents need a fresh serve target before start. Create a new agent with Run on relay mesh selected.";
|
||||
}
|
||||
|
||||
export async function startManagedAgentWithRules({
|
||||
agent,
|
||||
startManagedAgent,
|
||||
@@ -94,8 +82,9 @@ export async function startManagedAgentWithRules({
|
||||
agent: ManagedAgent;
|
||||
startManagedAgent: StartManagedAgent;
|
||||
}) {
|
||||
const relayMeshError = relayMeshAgentError(agent);
|
||||
if (relayMeshError) throw new Error(relayMeshError);
|
||||
// Relay-mesh agents are no longer blocked here: the backend start preflight
|
||||
// (ensure_relay_mesh_for_record) re-resolves a live serve target and dials
|
||||
// it, failing with an actionable error when no peer serves the model.
|
||||
await startManagedAgent(agent.pubkey);
|
||||
}
|
||||
|
||||
@@ -108,8 +97,6 @@ export async function respawnManagedAgentWithRules({
|
||||
startManagedAgent: StartManagedAgent;
|
||||
stopManagedAgent: StopManagedAgent;
|
||||
}) {
|
||||
const relayMeshError = relayMeshAgentError(agent);
|
||||
if (relayMeshError) throw new Error(relayMeshError);
|
||||
if (agent.backend.type === "local" && isManagedAgentActive(agent)) {
|
||||
await stopManagedAgent(agent.pubkey);
|
||||
}
|
||||
|
||||
@@ -4765,10 +4765,19 @@ async function handleStartManagedAgent(args: {
|
||||
}): Promise<RawManagedAgent> {
|
||||
const agent = getMockManagedAgent(args.pubkey);
|
||||
if (isRelayMeshManagedAgent(agent)) {
|
||||
// Model the backend start preflight (ensure_relay_mesh_for_record): a
|
||||
// saved relay-mesh agent re-resolves a live serve target for its model
|
||||
// and only fails when no peer currently serves it.
|
||||
const modelId = agent.env_vars?.OPENAI_COMPAT_MODEL;
|
||||
const hasLiveTarget =
|
||||
mockMeshState.admitted &&
|
||||
mockMeshState.models.some((model) => model.id === modelId);
|
||||
if (!hasLiveTarget) {
|
||||
throw new Error(
|
||||
"relay mesh agents cannot be started from saved state because the selected serve target is not persisted. Create a new agent with Run on relay mesh selected to refresh the target for http://127.0.0.1:9337/v1.",
|
||||
"relay mesh agents cannot be started from saved state because no live serve target is available for this model. Start serving on a mesh peer, or create a new agent with Run on relay mesh selected to refresh the target for http://127.0.0.1:9337/v1.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
if (agent.backend.type === "provider") {
|
||||
|
||||
@@ -278,7 +278,7 @@ test("a non-member cannot enable relay-mesh — membership is the gate", async (
|
||||
expect(seq).not.toContain("create_managed_agent");
|
||||
});
|
||||
|
||||
test("saved relay-mesh agents require a fresh serve target before manual start", async ({
|
||||
test("saved relay-mesh agents restart via the backend serve-target preflight", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoApp(page);
|
||||
@@ -330,18 +330,30 @@ test("saved relay-mesh agents require a fresh serve target before manual start",
|
||||
.toContain("stop_managed_agent");
|
||||
await expect(row).toContainText("stopped");
|
||||
|
||||
const before = (await commands(page)).length;
|
||||
// With a live serve target for the model, manual restart goes through:
|
||||
// the backend preflight re-resolves the target and the agent starts.
|
||||
await openManagedAgentActions(page, pubkey);
|
||||
await page.getByRole("menuitem", { name: "Spawn" }).click();
|
||||
await expect
|
||||
.poll(async () => await commands(page))
|
||||
.toContain("start_managed_agent");
|
||||
await expect(row).toContainText("running");
|
||||
|
||||
await openManagedAgentActions(page, pubkey);
|
||||
await page.getByRole("menuitem", { name: "Stop" }).click();
|
||||
await expect(row).toContainText("stopped");
|
||||
|
||||
// Without a live serve target, the backend preflight rejects the start
|
||||
// with an actionable error, surfaced as a toast; the agent stays stopped.
|
||||
await setMesh(page, { models: [] });
|
||||
await openManagedAgentActions(page, pubkey);
|
||||
await page.getByRole("menuitem", { name: "Spawn" }).click();
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator("[data-sonner-toast]")
|
||||
.filter({ hasText: "Relay-mesh agents need a fresh serve target" }),
|
||||
.filter({ hasText: "no live serve target is available" }),
|
||||
).toBeVisible();
|
||||
expect((await commands(page)).slice(before)).not.toContain(
|
||||
"start_managed_agent",
|
||||
);
|
||||
await expect(row).toContainText("stopped");
|
||||
|
||||
await expect(
|
||||
@@ -360,6 +372,6 @@ test("saved relay-mesh agents require a fresh serve target before manual start",
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
}, pubkey),
|
||||
).resolves.toContain("selected serve target is not persisted");
|
||||
).resolves.toContain("no live serve target is available");
|
||||
await expect(row).toContainText("stopped");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user