UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

78 lines (77 loc) • 3.28 kB
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 }; }