@sema-agent/core
Version:
Stateless, task-oriented AI agent core
78 lines (77 loc) • 3.28 kB
JavaScript
import { createAssistantMessageEventStream, } from "../internal/llm.js";
import { ZERO_USAGE } from "./errors.js";
function errorMessage(who, text) {
return {
role: "assistant",
content: [{ type: "text", text: "" }],
api: who.api,
provider: who.provider,
model: who.id,
stopReason: "error",
errorMessage: text,
usage: ZERO_USAGE,
timestamp: Date.now(),
};
}
const BLANK_IDENTITY = { api: "", provider: "", id: "" };
function spelled(word) {
return (typeof word === "object" && word !== null) || typeof word === "function" ? `[${typeof word}]` : String(word);
}
function routeKeyOf(model, by) {
return by === "api" ? model.api : model.provider;
}
function isBrain(value) {
return value !== null && typeof value === "object" && typeof value.stream === "function";
}
export function brainLaneOf(model, routes, by = "provider") {
const word = routeKeyOf(model, by);
return typeof word === "string" && Object.hasOwn(routes, word) ? word : undefined;
}
export function createRoutingBrain(routes, opts = {}) {
const by = opts.by ?? "provider";
const resolveTarget = (model) => {
const lane = brainLaneOf(model, routes, by);
if (lane === undefined)
return { target: opts.fallback };
const routed = routes[lane];
return isBrain(routed) ? { target: routed } : { broken: `route ${by}="${lane}" is mounted but its value is not a brain` };
};
const stream = (model, context, options) => {
const reject = (text, named) => {
const out = createAssistantMessageEventStream();
try {
const wordOf = (field) => (field === by && named !== undefined ? named.value : model[field]);
out.push({ type: "error", reason: "error", error: errorMessage({ api: wordOf("api"), provider: wordOf("provider"), id: wordOf("id") }, text) });
}
catch {
out.push({ type: "error", reason: "error", error: errorMessage(BLANK_IDENTITY, `${text} (the model's own words could not be read either)`) });
}
return out;
};
let word;
try {
word = { value: routeKeyOf(model, by) };
}
catch {
return reject(`createRoutingBrain: the model's ${by} could not be read`);
}
const named = word.value;
const asked = { api: named, provider: named };
let resolved;
try {
resolved = resolveTarget(asked);
}
catch {
return reject(`createRoutingBrain: the route table threw while ${by}="${spelled(named)}" was looked up`, word);
}
const target = "target" in resolved ? resolved.target : undefined;
if (!target)
return reject("broken" in resolved ? `createRoutingBrain: ${resolved.broken}` : `createRoutingBrain: no brain for ${by}="${spelled(named)}"`, word);
return target.stream(model, context, options);
};
const adjudicateRoute = (model, perModelAuth) => {
const resolved = resolveTarget(model);
return "target" in resolved ? resolved.target?.adjudicateRoute?.(model, perModelAuth) : undefined;
};
return { stream, adjudicateRoute };
}