+39
-12
@@ -9,31 +9,58 @@ export const workerNames = [
|
||||
"deep-hi",
|
||||
] as const;
|
||||
|
||||
const assignment = z.strictObject({
|
||||
const gptAssignment = z.strictObject({
|
||||
model: z.string().trim().min(1),
|
||||
effort: z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]),
|
||||
});
|
||||
|
||||
const customAssignment = gptAssignment.extend({
|
||||
model: z
|
||||
.string()
|
||||
.trim()
|
||||
.refine((model) => {
|
||||
const slash = model.indexOf("/");
|
||||
return slash > 0 && slash < model.length - 1;
|
||||
}, "model must use the provider/model convention"),
|
||||
});
|
||||
|
||||
const common = {
|
||||
provider: z.string().trim().min(1).default("openai"),
|
||||
lunaPriority: z.boolean().default(true),
|
||||
playwright: z.boolean().default(false),
|
||||
};
|
||||
|
||||
export const settings = z.discriminatedUnion("mode", [
|
||||
z.strictObject({ ...common, mode: z.literal("gpt") }),
|
||||
z
|
||||
.strictObject({
|
||||
...common,
|
||||
mode: z.literal("gpt"),
|
||||
provider: z.string().trim().min(1).default("openai"),
|
||||
lunaPriority: z.boolean().optional(),
|
||||
})
|
||||
.transform((value, context) => {
|
||||
if (value.provider !== "openai" && value.lunaPriority !== undefined) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
path: ["lunaPriority"],
|
||||
message: "lunaPriority is only supported by the openai provider",
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return value.provider === "openai"
|
||||
? { ...value, lunaPriority: value.lunaPriority ?? true }
|
||||
: value;
|
||||
}),
|
||||
z.strictObject({
|
||||
...common,
|
||||
mode: z.literal("custom"),
|
||||
agents: z.strictObject({
|
||||
"fast-lo": assignment,
|
||||
"fast-hi": assignment,
|
||||
"good-lo": assignment,
|
||||
"good-hi": assignment,
|
||||
"deep-lo": assignment,
|
||||
"deep-hi": assignment,
|
||||
"fast-lo": customAssignment,
|
||||
"fast-hi": customAssignment,
|
||||
"good-lo": customAssignment,
|
||||
"good-hi": customAssignment,
|
||||
"deep-lo": customAssignment,
|
||||
"deep-hi": customAssignment,
|
||||
// Deprecated compatibility field. Coordinate is derived from deep-lo/good-lo.
|
||||
coordinate: assignment.optional(),
|
||||
coordinate: customAssignment.optional(),
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
@@ -45,4 +72,4 @@ export const gptAgents = {
|
||||
"good-hi": { model: "gpt-5.6-sol", effort: "high" },
|
||||
"deep-lo": { model: "gpt-6-astra", effort: "low" },
|
||||
"deep-hi": { model: "gpt-6-astra", effort: "high" },
|
||||
} satisfies Record<(typeof workerNames)[number], z.infer<typeof assignment>>;
|
||||
} satisfies Record<(typeof workerNames)[number], z.infer<typeof gptAssignment>>;
|
||||
|
||||
+41
-9
@@ -3,7 +3,7 @@ import type { RpcRegistration } from "@opencode/plugin/promise/rpc";
|
||||
import type { ModelInfo } from "@opencode/client";
|
||||
import { z } from "zod";
|
||||
import { gptAgents, settings, workerNames } from "./config.js";
|
||||
import { discover } from "./provider.js";
|
||||
import { discover, modelKey } from "./provider.js";
|
||||
import { descriptions, routingGuide } from "./routing.js";
|
||||
import { QuotaTracker } from "./quota.js";
|
||||
import { QuotaRpc } from "./quota-rpc.js";
|
||||
@@ -50,6 +50,10 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
id: "stanp-opencode",
|
||||
async setup(context) {
|
||||
const selected = settings.parse({ mode: "gpt", ...context.options });
|
||||
const lunaPriority =
|
||||
selected.mode === "gpt" &&
|
||||
selected.provider === "openai" &&
|
||||
selected.lunaPriority === true;
|
||||
let models = new Map<string, { model: ModelInfo; serviceTier: string | undefined }>();
|
||||
let inherited: Agent.Info["permissions"] = Agent.Info.default(
|
||||
Agent.ID.make("build"),
|
||||
@@ -83,7 +87,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
continue;
|
||||
}
|
||||
if (event.type === "model.updated") {
|
||||
models = await discover(context, selected.provider);
|
||||
models = await discover(context, lunaPriority);
|
||||
await context.agent.reload();
|
||||
} else if (event.type === "agent.updated") {
|
||||
// The configured build transform may run after ours. Read the final
|
||||
@@ -118,13 +122,24 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
if (!controller.signal.aborted)
|
||||
console.error("stanp-opencode model update failed", error);
|
||||
});
|
||||
models = await discover(context, selected.provider);
|
||||
models = await discover(context, lunaPriority);
|
||||
const { data: initialAgents } = await context.agent.list();
|
||||
inherited =
|
||||
initialAgents.find((agent) => agent.id === "build")?.permissions ?? inherited;
|
||||
permissionsChanged(inherited);
|
||||
permissionKey = JSON.stringify(inherited);
|
||||
const assignments = selected.mode === "gpt" ? gptAgents : selected.agents;
|
||||
const assignments =
|
||||
selected.mode === "gpt"
|
||||
? Object.fromEntries(
|
||||
workerNames.map((name) => [
|
||||
name,
|
||||
{
|
||||
...gptAgents[name],
|
||||
provider: selected.provider,
|
||||
},
|
||||
]),
|
||||
)
|
||||
: selected.agents;
|
||||
let workers: (typeof workerNames)[number][] = [];
|
||||
const active = new Set<string>();
|
||||
const playwrightCommand = selected.playwright
|
||||
@@ -163,11 +178,26 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
});
|
||||
|
||||
await context.agent.transform((editor) => {
|
||||
const deep = models.has(assignments["deep-lo"].model);
|
||||
const coordinates = (assignment: { model: string }) => {
|
||||
if (selected.mode === "gpt")
|
||||
return { provider: selected.provider, model: assignment.model };
|
||||
const slash = assignment.model.indexOf("/");
|
||||
return {
|
||||
provider: assignment.model.slice(0, slash),
|
||||
model: assignment.model.slice(slash + 1),
|
||||
};
|
||||
};
|
||||
const availableFor = (
|
||||
assignment: (typeof assignments)[(typeof workerNames)[number]],
|
||||
) => {
|
||||
const { provider, model } = coordinates(assignment);
|
||||
return models.get(modelKey(provider, model));
|
||||
};
|
||||
const deep = availableFor(assignments["deep-lo"]) !== undefined;
|
||||
const candidates = workerNames.filter((name) => deep || !name.startsWith("deep-"));
|
||||
const coordinate = assignments[deep ? "deep-lo" : "good-lo"];
|
||||
workers = [coordinate, ...candidates.map((name) => assignments[name])].every(
|
||||
({ model }) => models.has(model),
|
||||
(assignment) => availableFor(assignment) !== undefined,
|
||||
)
|
||||
? candidates
|
||||
: [];
|
||||
@@ -176,7 +206,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
if (workers.length === 0) return;
|
||||
for (const name of workers) {
|
||||
const assignment = assignments[name];
|
||||
const available = models.get(assignment.model)!;
|
||||
const available = availableFor(assignment)!;
|
||||
editor.update(name, (agent) => {
|
||||
const hasVariant = available.model.variants.some(
|
||||
({ id }) => id === assignment.effort,
|
||||
@@ -196,7 +226,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
headers: {},
|
||||
body: {},
|
||||
};
|
||||
if (selected.lunaPriority && available.serviceTier)
|
||||
if (lunaPriority && available.serviceTier)
|
||||
agent.request.settings.serviceTier = available.serviceTier;
|
||||
agent.permissions = [
|
||||
...structuredClone(inherited),
|
||||
@@ -205,7 +235,7 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
});
|
||||
}
|
||||
editor.update("coordinate", (agent) => {
|
||||
const available = models.get(coordinate.model)!;
|
||||
const available = availableFor(coordinate)!;
|
||||
const hasVariant = available.model.variants.some(
|
||||
({ id }) => id === coordinate.effort,
|
||||
);
|
||||
@@ -224,6 +254,8 @@ const makeBasePlugin = (permissionsChanged: (permissions: Agent.Info["permission
|
||||
headers: {},
|
||||
body: {},
|
||||
};
|
||||
if (lunaPriority && available.serviceTier)
|
||||
agent.request.settings.serviceTier = available.serviceTier;
|
||||
agent.system = routingGuide(deep);
|
||||
agent.permissions = [
|
||||
{ action: "*", resource: "*", effect: "deny" },
|
||||
|
||||
+10
-3
@@ -20,13 +20,20 @@ function priority(model: ModelInfo) {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export async function discover(context: Plugin.Context, providerID: string) {
|
||||
export function modelKey(providerID: string, modelID: string) {
|
||||
return `${providerID}\0${modelID}`;
|
||||
}
|
||||
|
||||
export async function discover(context: Plugin.Context, detectPriority: boolean) {
|
||||
// Native lists already apply activation, integrations, configuration, and model transforms.
|
||||
const { data } = await context.model.list();
|
||||
const models = new Map<string, { model: ModelInfo; serviceTier: string | undefined }>();
|
||||
for (const model of data) {
|
||||
if (model.providerID === providerID && model.enabled)
|
||||
models.set(model.id, { model, serviceTier: priority(model) });
|
||||
if (model.enabled)
|
||||
models.set(modelKey(model.providerID, model.id), {
|
||||
model,
|
||||
serviceTier: detectPriority ? priority(model) : undefined,
|
||||
});
|
||||
}
|
||||
return models;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user