mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 08:22:20 +02:00
Set new agent concurrency default to five
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
98344a3642
commit
e783c88226
13 files changed
+59
-11
No files matched your search
@@ -619,7 +619,7 @@ Per-agent schedule fields in `adapter_config`:
|
||||
|
||||
- `enabled` boolean
|
||||
- `intervalSec` integer (minimum 30)
|
||||
- `maxConcurrentRuns` fixed at `1` for V1
|
||||
- `maxConcurrentRuns` integer; new agents default to `5`
|
||||
|
||||
Scheduler must skip invocation when:
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ export const AGENT_ROLE_LABELS: Record<AgentRole, string> = {
|
||||
general: "General",
|
||||
};
|
||||
|
||||
export const AGENT_DEFAULT_MAX_CONCURRENT_RUNS = 5;
|
||||
|
||||
export const AGENT_ICON_NAMES = [
|
||||
"bot",
|
||||
"cpu",
|
||||
|
||||
@@ -9,6 +9,7 @@ export {
|
||||
AGENT_ADAPTER_TYPES,
|
||||
AGENT_ROLES,
|
||||
AGENT_ROLE_LABELS,
|
||||
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
|
||||
AGENT_ICON_NAMES,
|
||||
ISSUE_STATUSES,
|
||||
INBOX_MINE_ISSUE_STATUSES,
|
||||
|
||||
@@ -483,6 +483,7 @@ describe("agent permission routes", () => {
|
||||
heartbeat: {
|
||||
enabled: false,
|
||||
intervalSec: 3600,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -520,6 +521,7 @@ describe("agent permission routes", () => {
|
||||
heartbeat: {
|
||||
enabled: false,
|
||||
intervalSec: 3600,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -2132,6 +2132,7 @@ describe("company portability", () => {
|
||||
runtimeConfig: {
|
||||
heartbeat: {
|
||||
enabled: false,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { and, desc, eq, inArray, not, sql } from "drizzle-orm";
|
||||
import {
|
||||
agentSkillSyncSchema,
|
||||
agentMineInboxQuerySchema,
|
||||
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
|
||||
createAgentKeySchema,
|
||||
createAgentHireSchema,
|
||||
createAgentSchema,
|
||||
@@ -524,6 +525,9 @@ export function agentRoutes(db: Db) {
|
||||
if (parseBooleanLike(heartbeat.enabled) == null) {
|
||||
heartbeat.enabled = false;
|
||||
}
|
||||
if (parseNumberLike(heartbeat.maxConcurrentRuns) == null) {
|
||||
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
|
||||
}
|
||||
|
||||
normalizedRuntimeConfig.heartbeat = heartbeat;
|
||||
return normalizedRuntimeConfig;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
issues,
|
||||
issueComments,
|
||||
} from "@paperclipai/db";
|
||||
import { isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared";
|
||||
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS, isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared";
|
||||
import { conflict, notFound, unprocessable } from "../errors.js";
|
||||
import { normalizeAgentPermissions } from "./agent-permissions.js";
|
||||
import { REDACTED_EVENT_VALUE, sanitizeRecord } from "../redaction.js";
|
||||
@@ -114,6 +114,25 @@ function hasConfigPatchFields(data: Partial<typeof agents.$inferInsert>) {
|
||||
return CONFIG_REVISION_FIELDS.some((field) => Object.prototype.hasOwnProperty.call(data, field));
|
||||
}
|
||||
|
||||
function parseFiniteNumberLike(value: unknown): number | null {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value;
|
||||
if (typeof value !== "string") return null;
|
||||
const parsed = Number(value.trim());
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
function normalizeRuntimeConfigForNewAgent(runtimeConfig: unknown): Record<string, unknown> {
|
||||
const normalizedRuntimeConfig = isPlainRecord(runtimeConfig) ? { ...runtimeConfig } : {};
|
||||
const heartbeat = isPlainRecord(normalizedRuntimeConfig.heartbeat)
|
||||
? { ...normalizedRuntimeConfig.heartbeat }
|
||||
: {};
|
||||
if (parseFiniteNumberLike(heartbeat.maxConcurrentRuns) == null) {
|
||||
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
|
||||
}
|
||||
normalizedRuntimeConfig.heartbeat = heartbeat;
|
||||
return normalizedRuntimeConfig;
|
||||
}
|
||||
|
||||
function diffConfigSnapshot(
|
||||
before: AgentConfigSnapshot,
|
||||
after: AgentConfigSnapshot,
|
||||
@@ -398,9 +417,10 @@ export function agentService(db: Db) {
|
||||
|
||||
const role = data.role ?? "general";
|
||||
const normalizedPermissions = normalizeAgentPermissions(data.permissions, role);
|
||||
const runtimeConfig = normalizeRuntimeConfigForNewAgent(data.runtimeConfig);
|
||||
const created = await db
|
||||
.insert(agents)
|
||||
.values({ ...data, name: uniqueName, companyId, role, permissions: normalizedPermissions })
|
||||
.values({ ...data, name: uniqueName, companyId, role, permissions: normalizedPermissions, runtimeConfig })
|
||||
.returning()
|
||||
.then((rows) => rows[0]);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import type {
|
||||
RoutineVariable,
|
||||
} from "@paperclipai/shared";
|
||||
import {
|
||||
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
|
||||
ISSUE_PRIORITIES,
|
||||
ISSUE_STATUSES,
|
||||
PROJECT_STATUSES,
|
||||
@@ -590,7 +591,7 @@ const RUNTIME_DEFAULT_RULES: Array<{ path: string[]; value: unknown }> = [
|
||||
{ path: ["heartbeat", "wakeOnAssignment"], value: true },
|
||||
{ path: ["heartbeat", "wakeOnAutomation"], value: true },
|
||||
{ path: ["heartbeat", "wakeOnDemand"], value: true },
|
||||
{ path: ["heartbeat", "maxConcurrentRuns"], value: 3 },
|
||||
{ path: ["heartbeat", "maxConcurrentRuns"], value: AGENT_DEFAULT_MAX_CONCURRENT_RUNS },
|
||||
];
|
||||
|
||||
const ADAPTER_DEFAULT_RULES_BY_TYPE: Record<string, Array<{ path: string[]; value: unknown }>> = {
|
||||
@@ -741,10 +742,20 @@ function clonePortableRecord(value: unknown) {
|
||||
return structuredClone(value) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function parseFiniteNumberLike(value: unknown): number | null {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value;
|
||||
if (typeof value !== "string") return null;
|
||||
const parsed = Number(value.trim());
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
function disableImportedTimerHeartbeat(runtimeConfig: unknown) {
|
||||
const next = clonePortableRecord(runtimeConfig) ?? {};
|
||||
const heartbeat = isPlainRecord(next.heartbeat) ? { ...next.heartbeat } : {};
|
||||
heartbeat.enabled = false;
|
||||
if (parseFiniteNumberLike(heartbeat.maxConcurrentRuns) == null) {
|
||||
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
|
||||
}
|
||||
next.heartbeat = heartbeat;
|
||||
return next;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,12 @@ import { execFile as execFileCallback } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { and, asc, desc, eq, getTableColumns, gt, inArray, isNull, or, sql } from "drizzle-orm";
|
||||
import type { Db } from "@paperclipai/db";
|
||||
import type { BillingType, ExecutionWorkspace, ExecutionWorkspaceConfig } from "@paperclipai/shared";
|
||||
import {
|
||||
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
|
||||
type BillingType,
|
||||
type ExecutionWorkspace,
|
||||
type ExecutionWorkspaceConfig,
|
||||
} from "@paperclipai/shared";
|
||||
import {
|
||||
agents,
|
||||
agentRuntimeState,
|
||||
@@ -83,7 +88,7 @@ const MAX_RUN_EVENT_PAYLOAD_STRING_CHARS = 16 * 1024;
|
||||
const MAX_RUN_EVENT_PAYLOAD_ARRAY_ITEMS = 50;
|
||||
const MAX_RUN_EVENT_PAYLOAD_OBJECT_KEYS = 100;
|
||||
const MAX_RUN_EVENT_PAYLOAD_DEPTH = 6;
|
||||
const HEARTBEAT_MAX_CONCURRENT_RUNS_DEFAULT = 1;
|
||||
const HEARTBEAT_MAX_CONCURRENT_RUNS_DEFAULT = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
|
||||
const HEARTBEAT_MAX_CONCURRENT_RUNS_MAX = 10;
|
||||
const DEFERRED_WAKE_CONTEXT_KEY = "_paperclipWakeContext";
|
||||
const WAKE_COMMENT_IDS_KEY = "wakeCommentIds";
|
||||
|
||||
@@ -84,7 +84,7 @@ test.describe("Onboarding wizard", () => {
|
||||
intervalSec: 300,
|
||||
wakeOnDemand: false,
|
||||
cooldownSec: 10,
|
||||
maxConcurrentRuns: 1,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
CompanySecret,
|
||||
EnvBinding,
|
||||
} from "@paperclipai/shared";
|
||||
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS } from "@paperclipai/shared";
|
||||
import type { AdapterModel } from "../api/agents";
|
||||
import { agentsApi } from "../api/agents";
|
||||
import { secretsApi } from "../api/secrets";
|
||||
@@ -921,7 +922,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
|
||||
value={eff(
|
||||
"heartbeat",
|
||||
"maxConcurrentRuns",
|
||||
Number(heartbeat.maxConcurrentRuns ?? 1),
|
||||
Number(heartbeat.maxConcurrentRuns ?? AGENT_DEFAULT_MAX_CONCURRENT_RUNS),
|
||||
)}
|
||||
onCommit={(v) => mark("heartbeat", "maxConcurrentRuns", v)}
|
||||
immediate
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("buildNewAgentRuntimeConfig", () => {
|
||||
intervalSec: 300,
|
||||
wakeOnDemand: true,
|
||||
cooldownSec: 10,
|
||||
maxConcurrentRuns: 1,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -27,7 +27,7 @@ describe("buildNewAgentRuntimeConfig", () => {
|
||||
intervalSec: 3600,
|
||||
wakeOnDemand: true,
|
||||
cooldownSec: 10,
|
||||
maxConcurrentRuns: 1,
|
||||
maxConcurrentRuns: 5,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS } from "@paperclipai/shared";
|
||||
import { defaultCreateValues } from "../components/agent-config-defaults";
|
||||
|
||||
export function buildNewAgentRuntimeConfig(input?: {
|
||||
@@ -10,7 +11,7 @@ export function buildNewAgentRuntimeConfig(input?: {
|
||||
intervalSec: input?.intervalSec ?? defaultCreateValues.intervalSec,
|
||||
wakeOnDemand: true,
|
||||
cooldownSec: 10,
|
||||
maxConcurrentRuns: 1,
|
||||
maxConcurrentRuns: AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user