Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/cydo/runtime/config/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ struct CydoConfig
@Optional string default_agent;
@Optional string default_task_type;
@Optional AgentConfig[string] agents;
/// Generate next-message suggestions (a small-model one-shot at each turn
/// end and on opening a live task). Set false to stop the backend from
/// creating them at all; title generation is separate and unaffected.
@Optional SetInfo!bool suggestions;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just do @Optional bool suggestions = true; ?

@Optional bool dev_mode;
@Optional string log_level = "info";
@Optional string system_keyword = "SYSTEM";
Expand Down
1 change: 1 addition & 0 deletions source/cydo/server/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ class App
));
derivedTextJobs = new DerivedTextJobs(DerivedTextJobsHost(
getTask: (int tid) => tid in tasks ? &tasks[tid] : null,
suggestionsEnabled: () => !config.suggestions.set || config.suggestions.value,
snapshotTaskIds: &snapshotTaskIdsForResume,
agentForTask: &agentForTask,
hasSubscribers: (int tid) => clientHub.hasSubscribers(tid),
Expand Down
6 changes: 6 additions & 0 deletions source/cydo/workflow/tasks/derived_text.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import cydo.workflow.history.abbrev : buildAbbreviatedHistoryFromStrings;
struct DerivedTextJobsHost
{
TaskData* delegate(int tid) getTask;
/// Whether suggestion generation is enabled at all (config `suggestions`);
/// null means enabled. Gating here stops the backend one-shots themselves,
/// not just the display.
bool delegate() suggestionsEnabled;
int[] delegate() snapshotTaskIds;
Agent delegate(int tid) agentForTask;
bool delegate(int tid) hasSubscribers;
Expand Down Expand Up @@ -144,6 +148,8 @@ public:

void generateSuggestions(int tid)
{
if (host_.suggestionsEnabled !is null && !host_.suggestionsEnabled())

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessarily defensive - will never be null?

return;
auto td = host_.getTask(tid);
if (td is null)
return;
Expand Down
47 changes: 47 additions & 0 deletions tests/e2e/suggestions-off.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
test,
expect,
enterSession,
sendMessage,
responseTimeout,
assistantText,
} from "./fixtures";
import { writeTestConfig } from "./test-config";

// `suggestions: false` stops the backend from generating next-message
// suggestions at all: the one-shot is never issued, so the suggestion bar
// never populates.
test("suggestions: false stops backend generation", async ({
page,
agentType,
}) => {
writeTestConfig(
"/tmp/playwright-home/.config/cydo/config.yaml",
`default_agent: ${agentType}
log_level: trace
suggestions: false
workspaces:
local:
root: /tmp/cydo-test-workspace
`,
);
// Give the backend's inotify watcher time to reload the config.
await page.waitForTimeout(500);

await enterSession(page);
await sendMessage(page, 'Please reply with "one"');
await expect(assistantText(page, "one")).toBeVisible({
timeout: responseTimeout(agentType),
});

// A second full turn bounds the wait without a wall-clock sleep: had the
// first turn generated suggestions, they would have rendered long before
// the second turn completes, since the generation one-shot answers from
// the same mock as the turns themselves.
await sendMessage(page, 'Please reply with "two"');
await expect(assistantText(page, "two")).toBeVisible({
timeout: responseTimeout(agentType),
});

await expect(page.locator(".btn-suggestion")).toHaveCount(0);
});