├── src ├── index.ts ├── workspaces │ ├── manager │ │ └── index.ts │ ├── restore │ │ ├── index.ts │ │ └── restorable.ts │ └── index.ts ├── agents │ ├── registry │ │ └── index.ts │ ├── supervisor │ │ └── workflow │ │ │ ├── __test__ │ │ │ ├── defaults.ts │ │ │ └── helpers │ │ │ │ └── withWorkflowLoggerMock.ts │ │ │ ├── helpers │ │ │ └── env.ts │ │ │ ├── workflow-composer │ │ │ ├── helpers │ │ │ │ ├── prompt.ts │ │ │ │ ├── task-step │ │ │ │ │ ├── helpers │ │ │ │ │ │ ├── assign-resource.ts │ │ │ │ │ │ └── assert.ts │ │ │ │ │ └── dto.ts │ │ │ │ └── resources │ │ │ │ │ ├── dto.ts │ │ │ │ │ └── utils.ts │ │ │ ├── task-initializer │ │ │ │ ├── agent-config-initializer │ │ │ │ │ ├── agent-instructions-builder │ │ │ │ │ │ ├── protocol.ts │ │ │ │ │ │ ├── dto.ts │ │ │ │ │ │ ├── prompt.test.ts │ │ │ │ │ │ └── __tests__ │ │ │ │ │ │ │ └── helpers │ │ │ │ │ │ │ └── create-example-input.ts │ │ │ │ │ ├── prompt.test.ts │ │ │ │ │ ├── dto.ts │ │ │ │ │ └── __tests__ │ │ │ │ │ │ └── helpers │ │ │ │ │ │ └── mocks.ts │ │ │ │ ├── task-config-initializer │ │ │ │ │ ├── prompt.test.ts │ │ │ │ │ ├── dto.ts │ │ │ │ │ └── __tests__ │ │ │ │ │ │ └── helpers │ │ │ │ │ │ └── mocks.ts │ │ │ │ └── dto.ts │ │ │ ├── dto.ts │ │ │ ├── task-run-initializer │ │ │ │ ├── prompt.test.ts │ │ │ │ ├── dto.ts │ │ │ │ ├── protocol.ts │ │ │ │ └── __tests__ │ │ │ │ │ └── helpers │ │ │ │ │ └── mocks.ts │ │ │ └── problem-decomposer │ │ │ │ ├── dto.ts │ │ │ │ ├── prompt.test.ts │ │ │ │ ├── protocol.ts │ │ │ │ └── __tests__ │ │ │ │ └── helpers │ │ │ │ └── create-example-input.ts │ │ │ ├── base │ │ │ ├── context.ts │ │ │ ├── retry │ │ │ │ └── retry.ts │ │ │ └── runnable.ts │ │ │ ├── fixtures │ │ │ ├── __test__ │ │ │ │ ├── poetry-song-analysis │ │ │ │ │ ├── tools.ts │ │ │ │ │ ├── task-step.ts │ │ │ │ │ ├── task-run.ts │ │ │ │ │ ├── task-config.ts │ │ │ │ │ └── agent-config.ts │ │ │ │ ├── f1-grand-prix │ │ │ │ │ ├── tools.ts │ │ │ │ │ ├── agent-config.ts │ │ │ │ │ ├── task-config.ts │ │ │ │ │ ├── task-run.ts │ │ │ │ │ ├── task-step.ts │ │ │ │ │ └── index.ts │ │ │ │ └── boston-trip │ │ │ │ │ ├── task-run.ts │ │ │ │ │ └── tools.ts │ │ │ ├── prompt │ │ │ │ ├── showcases │ │ │ │ │ ├── narrative-fusion │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ └── task-config.ts │ │ │ │ │ ├── who-is │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── agent-config.ts │ │ │ │ │ │ ├── task-config.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ ├── task-step.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── __template__ │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── agent-config.ts │ │ │ │ │ │ ├── task-config.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ ├── task-step.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── feedback-sentiment-analysis │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ ├── task-config.ts │ │ │ │ │ │ └── task-step.ts │ │ │ │ │ ├── asteroid-mining-feasibility │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ └── task-config.ts │ │ │ │ │ ├── medieval-charter-digitisation │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ └── task-config.ts │ │ │ │ │ ├── micro-grid-load-balancing │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ └── task-config.ts │ │ │ │ │ ├── beekeeping-site-analysis │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ ├── tools.ts │ │ │ │ │ │ └── task-config.ts │ │ │ │ │ ├── smart-farm-harvest-planner │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ └── tools.ts │ │ │ │ │ └── deep-sea-exploration │ │ │ │ │ │ ├── task-run.ts │ │ │ │ │ │ ├── task-config.ts │ │ │ │ │ │ └── tools.ts │ │ │ │ └── README.md │ │ │ ├── base │ │ │ │ ├── resource-fixtures.ts │ │ │ │ ├── fixtures.ts │ │ │ │ └── workflow-compose-fixtures.ts │ │ │ └── helpers │ │ │ │ ├── create-example.ts │ │ │ │ └── unwrap-task-step.ts │ │ │ ├── request-handler │ │ │ ├── prompt.test.ts │ │ │ └── protocol.ts │ │ │ ├── dto.ts │ │ │ ├── templates │ │ │ ├── example.ts │ │ │ └── chat-example.ts │ │ │ └── tool.ts │ ├── index.ts │ ├── base │ │ └── agent-factory.ts │ └── operator.ts ├── runtime │ ├── index.ts │ └── dto.ts ├── utils │ ├── disposable.ts │ ├── noop.ts │ ├── types.ts │ ├── service-locator.ts │ ├── text.ts │ ├── time.ts │ ├── file.ts │ ├── text.test.ts │ ├── objects.ts │ └── zod.ts ├── tasks │ └── index.ts ├── ui │ ├── main.ts │ ├── index.ts │ ├── chat-monitor │ │ └── workflow-popup │ │ │ ├── workflow-explorer │ │ │ ├── screens │ │ │ │ ├── workflow-screen.ts │ │ │ │ └── input-output-screen.ts │ │ │ └── components │ │ │ │ ├── text-area.ts │ │ │ │ ├── styled-text-area.ts │ │ │ │ └── phases.ts │ │ │ ├── helpers │ │ │ ├── flow-stepper-utils.ts │ │ │ └── data-stepper.test.ts │ │ │ └── config.ts │ ├── base │ │ └── monitor.ts │ └── controls │ │ ├── navigation.ts │ │ └── key-bindings.ts ├── access-control │ └── dto.ts ├── laml │ ├── index.ts │ ├── parser-output.test.ts │ ├── README.md │ └── parser-output.ts ├── base │ ├── dto.ts │ └── state │ │ ├── dto.ts │ │ └── base-state-logger.ts ├── tools │ ├── tavily │ │ ├── client.ts │ │ └── tavily-extract.ts │ ├── helpers │ │ └── tool-format.ts │ └── openf1 │ │ ├── list-seasons.ts │ │ ├── get-driver.ts │ │ ├── get-current-season-detail.ts │ │ ├── list-grandprix-positions.ts │ │ ├── get-season-detail.ts │ │ └── get-grand-prix-detail.ts ├── todo └── helpers │ └── llm.ts ├── types └── neo-blessed │ └── index.d.ts ├── docs └── assets │ ├── beekeeper-dark.png │ ├── beekeeper-light.png │ └── beekeeper-diagram.jpg ├── examples ├── tsconfig.json └── ui │ ├── chat-monitor │ └── chat-monitor.ts │ ├── helpers │ └── log.ts │ └── shared │ └── help-bar.ts ├── .github ├── workflows │ ├── build.yml │ └── release.yml └── actions │ └── setup │ └── action.yml ├── tsconfig.build.json ├── MAINTAINERS.md ├── SECURITY.md ├── prettier.config.js ├── eslint.config.js ├── .env.template ├── tsconfig.json └── vitest.config.ts /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@runtime/index.js"; 2 | -------------------------------------------------------------------------------- /src/workspaces/manager/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./manager.js"; 2 | -------------------------------------------------------------------------------- /src/workspaces/restore/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./restorable.js"; 2 | -------------------------------------------------------------------------------- /src/agents/registry/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./registry.js"; 2 | export * from "./tool.js"; 3 | -------------------------------------------------------------------------------- /types/neo-blessed/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "neo-blessed" { 2 | export * from "blessed"; 3 | } 4 | -------------------------------------------------------------------------------- /docs/assets/beekeeper-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i-am-bee/beekeeper/HEAD/docs/assets/beekeeper-dark.png -------------------------------------------------------------------------------- /docs/assets/beekeeper-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i-am-bee/beekeeper/HEAD/docs/assets/beekeeper-light.png -------------------------------------------------------------------------------- /src/runtime/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dto.js"; 2 | export * from "./factory.js"; 3 | export * from "./runtime.js"; 4 | -------------------------------------------------------------------------------- /src/utils/disposable.ts: -------------------------------------------------------------------------------- 1 | export interface Disposable { 2 | get disposed(): boolean; 3 | dispose(): void; 4 | } 5 | -------------------------------------------------------------------------------- /docs/assets/beekeeper-diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i-am-bee/beekeeper/HEAD/docs/assets/beekeeper-diagram.jpg -------------------------------------------------------------------------------- /src/tasks/index.ts: -------------------------------------------------------------------------------- 1 | export * as tasksTool from "./tool.js"; 2 | export * as tasksStateLogger from "./state/logger.js"; 3 | -------------------------------------------------------------------------------- /src/utils/noop.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-empty-function 2 | export const noop = () => {}; 3 | -------------------------------------------------------------------------------- /src/ui/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "beeai-framework"; 2 | import { Monitor } from "./monitor.js"; 3 | 4 | new Monitor("Bee Supervisor Monitor", Logger.root).start("./output"); 5 | -------------------------------------------------------------------------------- /src/access-control/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const PermissionSchema = z.enum(["read", "write", "execute"]); 4 | export type Permission = z.infer; 5 | -------------------------------------------------------------------------------- /src/laml/index.ts: -------------------------------------------------------------------------------- 1 | export * as dto from "./dto.js"; 2 | export * from "./parser-output.js"; 3 | export * from "./protocol.js"; 4 | export * from "./parser.js"; 5 | export * from "./utils.js"; 6 | -------------------------------------------------------------------------------- /src/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./agent-monitor/monitor.js"; 2 | export * from "./task-monitor/monitor.js"; 3 | export * from "./chat-monitor/chat-monitor.js"; 4 | export * from "./monitor.js"; 5 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/workflow-explorer/screens/workflow-screen.ts: -------------------------------------------------------------------------------- 1 | import { ContainerComponent } from "@/ui/base/monitor.js"; 2 | 3 | export class WorkflowScreen extends ContainerComponent {} 4 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/__test__/defaults.ts: -------------------------------------------------------------------------------- 1 | import { AgentIdValue } from "@/agents/registry/dto.js"; 2 | 3 | export const SUPERVISOR_AGENT_ID = 4 | "supervisor:beekeeper[1]:1" satisfies AgentIdValue; 5 | -------------------------------------------------------------------------------- /src/workspaces/index.ts: -------------------------------------------------------------------------------- 1 | import * as workspaceManager from "./manager/index.js"; 2 | import * as restoration from "./restore/index.js"; 3 | 4 | export default { 5 | workspaceManager, 6 | restoration, 7 | } as const; 8 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/helpers/env.ts: -------------------------------------------------------------------------------- 1 | import { parseEnv } from "beeai-framework/internals/env"; 2 | 3 | export const examplesEnabled = () => 4 | !parseEnv.asBoolean("DEV_SUPERVISOR_WORKFLOW_DISABLE_EXAMPLES", false); 5 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "..", 5 | "rootDir": "..", 6 | }, 7 | "exclude": [ 8 | "../tests", 9 | "../**/*.test.ts" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/prompt.ts: -------------------------------------------------------------------------------- 1 | import { format } from "date-fns"; 2 | 3 | export function groundInTime(prompt: string): string { 4 | return `Current time: ${format(new Date(), "EEEE, MMMM do, yyyy 'at' h:mm a")}!\n\n${prompt}`; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | // Extract type of an array items 2 | export type ArrayItemsType = T[number]; 3 | 4 | export type DeepPartial = T extends object // only dive in if it’s an object/array/function 5 | ? { [K in keyof T]?: DeepPartial } 6 | : T; 7 | -------------------------------------------------------------------------------- /examples/ui/chat-monitor/chat-monitor.ts: -------------------------------------------------------------------------------- 1 | // import { ChatMonitor } from "../../../src/ui/chat-monitor/chat-monitor.js"; 2 | // import { getLogger } from "../helpers/log.js"; 3 | 4 | // new ChatMonitor( 5 | // { kind: "screen", title: "Runtime Chat Interface" }, 6 | // getLogger(true), 7 | // ); 8 | -------------------------------------------------------------------------------- /src/agents/index.ts: -------------------------------------------------------------------------------- 1 | import * as agentRegistry from "./registry/index.js"; 2 | import * as stateLogger from "./state/logger.js"; 3 | import * as supervisor from "./supervisor/react-agent/supervisor.js"; 4 | import * as operator from "./operator.js"; 5 | 6 | export { agentRegistry, stateLogger, supervisor, operator }; 7 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/base/context.ts: -------------------------------------------------------------------------------- 1 | import { AgentUpdateCallback } from "@/agents/agent-factory.js"; 2 | import { AgentIdValue } from "@/agents/registry/dto.js"; 3 | import { ChatModel } from "beeai-framework"; 4 | 5 | export interface Context { 6 | llm: ChatModel; 7 | actingAgentId: AgentIdValue; 8 | onUpdate: AgentUpdateCallback; 9 | } 10 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/poetry-song-analysis/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "../../../workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../base/fixtures.js"; 3 | 4 | const ENTRIES = [] as const satisfies AgentAvailableTool[]; 5 | 6 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 7 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/narrative-fusion/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [] as const satisfies AgentAvailableTool[]; 5 | 6 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 7 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/agent-instructions-builder/protocol.ts: -------------------------------------------------------------------------------- 1 | import * as laml from "@/laml/index.js"; 2 | 3 | export const protocol = laml.ProtocolBuilder.new() 4 | .text({ 5 | name: "INSTRUCTIONS", 6 | description: 7 | "Natural language but structured text instructs on how agent should act", 8 | }) 9 | .build(); 10 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/request-handler/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { prompt } from "./prompt.js"; 3 | import { readFileSync } from "fs"; 4 | import { resolve } from "path"; 5 | 6 | describe(`Prompt`, () => { 7 | it(`should not change`, () => { 8 | const p = prompt(); 9 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/base/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const DateStringSchema = z.union([ 4 | z.string().transform((str) => new Date(str)), 5 | z.date(), 6 | ]); 7 | export type DateString = z.infer; 8 | 9 | export const OperationResultSchema = z.object({ 10 | relatedId: z.string(), 11 | success: z.boolean(), 12 | errorMessage: z.string().optional(), 13 | }); 14 | export type OperationResult = z.infer; 15 | -------------------------------------------------------------------------------- /src/tools/tavily/client.ts: -------------------------------------------------------------------------------- 1 | import { tavily, TavilyClient } from "@tavily/core"; 2 | import { getEnv } from "beeai-framework/internals/env"; 3 | 4 | let client: TavilyClient | null = null; 5 | 6 | export function getClient() { 7 | if (client) { 8 | return client; 9 | } 10 | 11 | const apiKey = getEnv("TAVILY_API_KEY"); 12 | if (apiKey == null || !apiKey.length) { 13 | throw new Error(`Missing 'TAVILY_API_KEY'`); 14 | } 15 | 16 | client = tavily({ apiKey }); 17 | return client; 18 | } 19 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/base/resource-fixtures.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TaskStep, 3 | TaskStepResource, 4 | } from "../../workflow-composer/helpers/task-step/dto.js"; 5 | import { createFixtures } from "./fixtures.js"; 6 | 7 | export function createResourceFixtures(...entries: TaskStepResource[]) { 8 | return createFixtures(entries, ({ type }) => type); 9 | } 10 | 11 | export type TaskStepWithVariousResource = Omit & { 12 | resource: ReturnType; 13 | }; 14 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/task-step/helpers/assign-resource.ts: -------------------------------------------------------------------------------- 1 | import { TaskStepWithVariousResource } from "@/agents/supervisor/workflow/fixtures/base/resource-fixtures.js"; 2 | import { TaskStep, TaskStepResource } from "../dto.js"; 3 | import { clone } from "remeda"; 4 | 5 | export function assignResource( 6 | step: TaskStep, 7 | resource: R, 8 | ): Omit & { resource: R } { 9 | return clone({ 10 | ...step, 11 | resource, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/base/state/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const LogUpdateSchema = (dataSchema: TData) => 4 | z.object({ 5 | timestamp: z.union([z.string(), z.date()]), 6 | data: dataSchema, 7 | }); 8 | export type LogUpdate = z.infer< 9 | ReturnType> 10 | >; 11 | 12 | export const LogInitSchema = z.object({ 13 | timestamp: z.union([z.string(), z.date()]), 14 | type: z.literal("@log_init"), 15 | }); 16 | export type LogInit = z.infer; 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build with mise 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | # Use the mise setup action 17 | - name: Setup mise 18 | uses: ./.github/actions/setup 19 | with: 20 | cache-key-prefix: 'beekeeper' 21 | node-version: '22' 22 | 23 | - name: Build project 24 | run: mise run build -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/dto.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunIdValueSchema, TaskRunSchema } from "@/tasks/manager/dto.js"; 2 | import { z } from "zod"; 3 | 4 | export const WorkflowComposerInputSchema = z.object({ 5 | originTaskRunId: TaskRunIdValueSchema, 6 | input: z.string(), 7 | }); 8 | export type WorkflowComposerInput = z.infer; 9 | 10 | export const WorkflowComposerOutputSchema = z.array(TaskRunSchema); 11 | export type WorkflowComposerOutput = z.infer< 12 | typeof WorkflowComposerOutputSchema 13 | >; 14 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: `TBD`, 7 | description: `TBD`, 8 | toolInput: `TBD`, 9 | }, 10 | { 11 | toolName: `TBD`, 12 | description: `TBD`, 13 | toolInput: `TBD`, 14 | }, 15 | ] as const satisfies AgentAvailableTool[]; 16 | 17 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 18 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "sourceRoot": "src", 6 | "types": [ 7 | "node", 8 | "neo-blessed" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ], 14 | "exclude": [ 15 | "**/*.test.ts", 16 | "**/*.spec.ts", 17 | "**/*.fixture.ts", // Exclude fixture files by pattern 18 | "**/*.fixtures.ts", 19 | "**/__fixtures__/**", // Exclude fixture directories 20 | "**/__mocks__/**", 21 | "**/__tests__/**", 22 | "tests" 23 | ] 24 | } -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-run-initializer/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { prompt } from "./prompt.js"; 3 | import { readFileSync } from "fs"; 4 | import { resolve } from "path"; 5 | 6 | describe(`Prompt`, () => { 7 | it(`Sample`, () => { 8 | const p = prompt({ 9 | previousSteps: [], 10 | resources: { 11 | tools: [], 12 | agents: [], 13 | tasks: [], 14 | taskRuns: [], 15 | }, 16 | }); 17 | 18 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | A repository maintainer is a committer with the additional privilege of merging pull requests into the main branch of this repository. 4 | 5 | ## Current Maintainers 6 | 7 | Maintainers are listed in alphabetical order by last name. 8 | 9 | | Name | GitHub Username | 10 | | ---- | ---- | 11 | | Aleš Kalfas | [aleskalfas](https://github.com/aleskalfas) | 12 | | Jenna Winkler | [jenna-winkler](https://github.com/jenna-winkler) | 13 | 14 | For more details on the process of becoming a maintainer, see the [Governance](https://github.com/i-am-bee/community/blob/main/GOVERNANCE.md) document. 15 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { prompt } from "./prompt.js"; 3 | import { readFileSync } from "fs"; 4 | import { resolve } from "path"; 5 | 6 | describe(`Prompt`, () => { 7 | it(`Sample`, () => { 8 | const p = prompt({ 9 | previousSteps: [], 10 | resources: { 11 | tools: [], 12 | agents: [], 13 | tasks: [], 14 | taskRuns: [], 15 | }, 16 | }); 17 | 18 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report vulnerabilities, you can privately report a potential security issue 6 | via the GitHub security vulnerabilities feature. This can be done here: 7 | 8 | https://github.com/i-am-bee/beekeeper/security/advisories 9 | 10 | Please do **not** open a public issue about a potential security vulnerability. 11 | 12 | You can find more details on the security vulnerability feature in the GitHub 13 | documentation here: 14 | 15 | https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability 16 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/problem-decomposer/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ResourcesSchema } from "../helpers/resources/dto.js"; 3 | import { TaskStepSchema } from "../helpers/task-step/dto.js"; 4 | 5 | export const ProblemDecomposerInputSchema = z.object({ 6 | resources: ResourcesSchema, 7 | request: z.string(), 8 | }); 9 | export type ProblemDecomposerInput = z.infer< 10 | typeof ProblemDecomposerInputSchema 11 | >; 12 | 13 | export const ProblemDecomposerOutputSchema = z.array(TaskStepSchema); 14 | export type ProblemDecomposerOutput = z.infer< 15 | typeof ProblemDecomposerOutputSchema 16 | >; 17 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/workflow-explorer/components/text-area.ts: -------------------------------------------------------------------------------- 1 | import blessed from "neo-blessed"; 2 | 3 | export class TextArea { 4 | private _element: blessed.Widgets.BoxElement; 5 | 6 | get element() { 7 | return this._element; 8 | } 9 | 10 | constructor(parent: blessed.Widgets.Node, style?: any) { 11 | this._element = blessed.box({ 12 | parent, 13 | width: "100%", 14 | height: "100%", 15 | focusable: false, 16 | keys: false, 17 | mouse: false, 18 | tags: true, 19 | hidden: false, 20 | style, 21 | }); 22 | this._element.setContent("AAAA"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/tools/helpers/tool-format.ts: -------------------------------------------------------------------------------- 1 | import { StringToolOutput } from "beeai-framework"; 2 | import { stringify } from "yaml"; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 5 | export type FormattedToolOutput = StringToolOutput; // Placeholder for future output types 6 | 7 | export function formatToolDescription({ 8 | example, 9 | description, 10 | }: { 11 | description: string; 12 | example: T; 13 | }): string { 14 | return `${description}\n\nOutput example:\n${stringify(example)}`; 15 | } 16 | 17 | export function formatToolOutput(output: T): FormattedToolOutput { 18 | return new StringToolOutput(stringify(output)); 19 | } 20 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/poetry-song-analysis/task-step.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { createFixtures } from "../../base/fixtures.js"; 3 | import { TaskStepWithVariousResource } from "../../base/resource-fixtures.js"; 4 | import toolsFixtures from "./tools.js"; 5 | import agentsFixtures from "./agent-config.js"; 6 | import tasksFixtures from "./task-config.js"; 7 | import taskRunsFixtures from "./task-run.js"; 8 | 9 | // type ToolName = FixtureName; 10 | 11 | const ENTRIES = [] as const satisfies TaskStepWithVariousResource[]; 12 | 13 | const fixtures = createFixtures(ENTRIES, ({ step }) => step); 14 | export default fixtures; 15 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ResourcesSchema } from "../helpers/resources/dto.js"; 3 | import { TaskStepSchema } from "../helpers/task-step/dto.js"; 4 | 5 | export const TaskInitializerInputSchema = z.object({ 6 | resources: ResourcesSchema, 7 | previousSteps: z.array(TaskStepSchema), 8 | taskStep: TaskStepSchema, 9 | }); 10 | export type TaskInitializerInput = z.infer; 11 | 12 | export const TaskInitializerOutputSchema = z.object({ 13 | resources: ResourcesSchema, 14 | taskStep: TaskStepSchema, 15 | }); 16 | export type TaskInitializerOutput = z.infer; 17 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/workflow-explorer/components/styled-text-area.ts: -------------------------------------------------------------------------------- 1 | import { clone } from "remeda"; 2 | import { TextArea } from "./text-area.js"; 3 | import blessed from "neo-blessed"; 4 | 5 | export enum Style { 6 | USER_INPUT = "user-input", 7 | USER_OUTPUT = "user-output", 8 | } 9 | 10 | const styles: Record = { 11 | [Style.USER_INPUT]: { 12 | fg: "white", 13 | bg: "black", 14 | }, 15 | [Style.USER_OUTPUT]: { 16 | fg: "green", 17 | bg: "red", 18 | }, 19 | } as const; 20 | 21 | export class StyledTextArea extends TextArea { 22 | constructor(parent: blessed.Widgets.Node, style: Style) { 23 | super(parent, clone(styles[style] || styles[Style.USER_INPUT])); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "../../../workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: `TBD`, 7 | description: `TBD`, 8 | toolInput: `TBD`, 9 | }, 10 | { 11 | toolName: `TBD`, 12 | description: `TBD`, 13 | toolInput: `TBD`, 14 | }, 15 | { 16 | toolName: `TBD`, 17 | description: `TBD`, 18 | toolInput: `TBD`, 19 | }, 20 | { 21 | toolName: `TBD`, 22 | description: `TBD`, 23 | toolInput: `TBD`, 24 | }, 25 | ] as const satisfies AgentAvailableTool[]; 26 | 27 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | permissions: 9 | contents: read 10 | id-token: write 11 | 12 | jobs: 13 | publish-npm: 14 | name: Publish on NPM 15 | if: github.event.base_ref == 'refs/heads/main' 16 | runs-on: ubuntu-latest 17 | environment: release 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: ./.github/actions/setup 21 | - run: mise build 22 | - run: | 23 | echo "//registry.npmjs.org/:_authToken=${{secrets.NPM_TOKEN}}" > ~/.npmrc 24 | pnpm publish --access public --no-git-checks 25 | working-directory: . 26 | env: 27 | NPM_CONFIG_PROVENANCE: true # https://github.com/pnpm/pnpm/issues/6435#issuecomment-1518397267 -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: `TBD`, 7 | description: `TBD`, 8 | toolInput: `TBD`, 9 | }, 10 | { 11 | toolName: `TBD`, 12 | description: `TBD`, 13 | toolInput: `TBD`, 14 | }, 15 | { 16 | toolName: `TBD`, 17 | description: `TBD`, 18 | toolInput: `TBD`, 19 | }, 20 | { 21 | toolName: `TBD`, 22 | description: `TBD`, 23 | toolInput: `TBD`, 24 | }, 25 | ] as const satisfies AgentAvailableTool[]; 26 | 27 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 28 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/dto.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunIdValueSchema } from "@/tasks/manager/dto.js"; 2 | import { z } from "zod"; 3 | 4 | export const AgentUpdateCallbackSchema = z 5 | .function() 6 | .args(z.string(), z.string()) 7 | .returns(z.void()); 8 | export type AgentUpdateCallback = z.infer; 9 | 10 | export const SupervisorWorkflowInputSchema = z.object({ 11 | prompt: z.string(), 12 | originTaskRunId: TaskRunIdValueSchema, 13 | onUpdate: AgentUpdateCallbackSchema, 14 | }); 15 | export type SupervisorWorkflowInput = z.infer< 16 | typeof SupervisorWorkflowInputSchema 17 | >; 18 | 19 | export const SupervisorWorkflowOutputSchema = z.string(); 20 | export type SupervisorWorkflowOutput = z.infer< 21 | typeof SupervisorWorkflowOutputSchema 22 | >; 23 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 © BeeAI a Series of LF Projects, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export default { 18 | printWidth: 80, 19 | trailingComma: "all", 20 | proseWrap: "always", 21 | }; 22 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/helpers/create-example.ts: -------------------------------------------------------------------------------- 1 | import * as laml from "@/laml/index.js"; 2 | import { Resources } from "../../workflow-composer/helpers/resources/dto.js"; 3 | import { TaskStep } from "../../workflow-composer/helpers/task-step/dto.js"; 4 | import { WorkflowComposeFixture } from "../base/workflow-compose-fixtures.js"; 5 | 6 | export interface BaseCreateExampleInput { 7 | step: Parameters[0]; 8 | fixtures: F; 9 | subtitle?: string; 10 | note?: string; 11 | } 12 | 13 | export interface ExampleInput

{ 14 | title: string; 15 | subtitle: string; 16 | user: string; 17 | context: { 18 | previousSteps: TaskStep[]; 19 | resources: Resources; 20 | }; 21 | example: laml.ProtocolResult

; 22 | } 23 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigSchema } from "@/agents/registry/dto.js"; 2 | import { TaskConfigSchema, TaskRunSchema } from "@/tasks/manager/dto.js"; 3 | import { z } from "zod"; 4 | 5 | export const AgentAvailableToolSchema = z.object({ 6 | toolName: z.string(), 7 | description: z.string(), 8 | toolInput: z.string().optional(), 9 | }); 10 | export type AgentAvailableTool = z.infer; 11 | 12 | export const ResourcesSchema = z.object({ 13 | tools: z.array(AgentAvailableToolSchema).readonly(), 14 | agents: z.array(AgentConfigSchema).readonly(), 15 | tasks: z.array(TaskConfigSchema).readonly(), 16 | taskRuns: z.array(TaskRunSchema).readonly(), 17 | }); 18 | export type Resources = z.infer; 19 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/problem-decomposer/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "fs"; 2 | import { resolve } from "path"; 3 | import { describe, expect, it } from "vitest"; 4 | import boston_trip_fixtures from "@/agents/supervisor/workflow/fixtures/__test__/boston-trip/index.js"; 5 | import { prompt } from "./prompt.js"; 6 | 7 | describe(`Prompt`, () => { 8 | it(`should not change`, () => { 9 | const fixtures = boston_trip_fixtures; 10 | const p = prompt({ 11 | request: fixtures.request, 12 | resources: { 13 | tools: fixtures.tools.values, 14 | agents: fixtures.agents.values, 15 | tasks: fixtures.tasks.values, 16 | taskRuns: [], 17 | }, 18 | }); 19 | 20 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/helpers/flow-stepper-utils.ts: -------------------------------------------------------------------------------- 1 | export function getArrayOnPath( 2 | path: (string | number)[], 3 | readState: () => any, 4 | ): any[] { 5 | const result = path.reduce((current, key) => current?.[key], readState()); 6 | if (!Array.isArray(result)) { 7 | throw new Error(`Value on path ${path.join(".")} is not an array`); 8 | } 9 | return result; 10 | } 11 | 12 | export function getValueOnPath( 13 | path: (string | number)[], 14 | readState: () => any, 15 | ): any { 16 | const result = path.reduce((current, key) => current?.[key], readState()); 17 | return result; 18 | } 19 | 20 | export function stringifyPath(path: (string | number)[]): string { 21 | return path 22 | .map((part) => (typeof part === "string" ? part : `[${part}]`)) 23 | .join(".") 24 | .replaceAll(".[", "["); 25 | } 26 | -------------------------------------------------------------------------------- /examples/ui/helpers/log.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "beeai-framework"; 2 | import path from "path"; 3 | import pino from "pino"; 4 | 5 | const LEVEL = "debug"; 6 | 7 | export function getLogger(logToFile = false) { 8 | return logToFile 9 | ? new Logger( 10 | { 11 | name: "ui", 12 | level: LEVEL, 13 | }, 14 | pino({ 15 | level: LEVEL, 16 | transport: { 17 | target: "pino-pretty", // the built-in prettifier 18 | options: { 19 | singleLine: true, // one physical line per record 20 | colorize: false, 21 | destination: path.join("examples/ui", "ui.log"), // write straight to a file 22 | append: false, 23 | }, 24 | }, 25 | }), 26 | ) 27 | : Logger.root.child({ name: "app", level: LEVEL }); 28 | } 29 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/agent-instructions-builder/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ResourcesSchema } from "../../../helpers/resources/dto.js"; 3 | import { TaskStepSchema } from "../../../helpers/task-step/dto.js"; 4 | import { AgentConfigTinyDraftSchema } from "../dto.js"; 5 | 6 | export const AgentInstructionsBuilderInputSchema = z.object({ 7 | resources: ResourcesSchema, 8 | previousSteps: z.array(TaskStepSchema), 9 | agentConfigDraft: AgentConfigTinyDraftSchema, 10 | taskStep: TaskStepSchema, 11 | }); 12 | export type AgentInstructionsBuilderInput = z.infer< 13 | typeof AgentInstructionsBuilderInputSchema 14 | >; 15 | 16 | export const AgentInstructionsBuilderOutputSchema = z.string(); 17 | export type AgentInstructionsBuilderOutput = z.infer< 18 | typeof AgentInstructionsBuilderOutputSchema 19 | >; 20 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/__test__/helpers/withWorkflowLoggerMock.ts: -------------------------------------------------------------------------------- 1 | import { vi } from "vitest"; 2 | 3 | export default () => 4 | vi.mock("@/agents/supervisor/workflow/state/logger.ts", async () => { 5 | const actual = await vi.importActual< 6 | typeof import("@/agents/supervisor/workflow/state/logger.ts") 7 | >("@/agents/supervisor/workflow/state/logger.ts"); 8 | 9 | const fakeLogger = new Proxy>( 10 | {}, 11 | { 12 | get(target, prop) { 13 | if (!(prop in target)) { 14 | target[prop] = vi.fn(); 15 | } 16 | return target[prop]; 17 | }, 18 | }, 19 | ); 20 | 21 | return { 22 | ...actual, 23 | SupervisorWorkflowStateLogger: { 24 | ...actual.SupervisorWorkflowStateLogger, 25 | getInstance: vi.fn(() => fakeLogger), 26 | }, 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /src/todo: -------------------------------------------------------------------------------- 1 | ☐ Problem decomposer 2 | - It doesn't connect inputs/outputs between steps correctly. 3 | - Sometimes missing mark [source: assumed] - It should be rewrite to produce well defined inputs/outputs. Inputs should has a type user/generated all well connected to the specific attribute in request or output from previous step. 4 | 5 | ☐ Logic fixer 6 | - Add step after the problem decomposer to fix the logic of the generated code. E.g. cyclic graph, missing conditional logic, unused outputs, missing deliverables etc.. 7 | 8 | ☐ Input negotiator 9 | - User request should be handle by a negotiator that will ask user for more details if needed (when request handler need clarification, or problemDecomposer creates step with grounded inputs). 10 | 11 | ☐ Disaster relive showcase 12 | - It is too massive and uncompleted 13 | 14 | ☐ Boston trip 15 | - Request handler and problem decomposer should failed due to missing date 16 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/poetry-song-analysis/task-run.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 3 | 4 | import taskConfigFixtures from "./task-config.js"; 5 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 6 | import { addTaskRunMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 7 | 8 | type TaskType = FixtureName; 9 | 10 | const ENTRIES = [ 11 | // { 12 | // taskType: "retrieve_field_metadata" satisfies TaskType, 13 | // taskRunInput: `{ "field_identifier": "South Field" }`, 14 | // taskRunNum: 1, 15 | // }, 16 | ] as const satisfies TaskRunMinimal[]; 17 | 18 | export default createFixtures( 19 | addTaskRunMissingAttrs(ENTRIES), 20 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 21 | ); 22 | -------------------------------------------------------------------------------- /src/runtime/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { AgentSchema } from "@/agents/registry/dto.js"; 3 | import { TaskRunSchema } from "@/tasks/manager/dto.js"; 4 | 5 | export const BaseRuntimeOutputSchema = z.object({ 6 | agent: AgentSchema.optional(), 7 | taskRun: TaskRunSchema, 8 | text: z.string(), 9 | }); 10 | export type BaseRuntimeOutput = z.infer; 11 | 12 | export const RuntimeProgressOutputSchema = BaseRuntimeOutputSchema.extend({ 13 | kind: z.literal("progress"), 14 | }); 15 | export type RuntimeProgressOutput = z.infer; 16 | 17 | export const RuntimeFinalOutputSchema = BaseRuntimeOutputSchema.extend({ 18 | kind: z.literal("final"), 19 | }); 20 | export type RuntimeFinalOutput = z.infer; 21 | 22 | export const RuntimeOutputSchema = z.discriminatedUnion("kind", [ 23 | RuntimeProgressOutputSchema, 24 | RuntimeFinalOutputSchema, 25 | ]); 26 | export type RuntimeOutput = z.infer; 27 | -------------------------------------------------------------------------------- /src/utils/service-locator.ts: -------------------------------------------------------------------------------- 1 | export class ServiceLocator { 2 | private static instance: ServiceLocator | null; 3 | private services = new Map(); 4 | private disposed = false; 5 | 6 | public static getInstance(): ServiceLocator { 7 | if (!ServiceLocator.instance) { 8 | ServiceLocator.instance = new ServiceLocator(); 9 | } 10 | return ServiceLocator.instance; 11 | } 12 | 13 | public register( 14 | constructor: new (...args: any[]) => T, 15 | instance: T, 16 | ): void { 17 | this.services.set(constructor, instance); 18 | } 19 | 20 | public get(constructor: new (...args: any[]) => T): T { 21 | const service = this.services.get(constructor); 22 | if (!service) { 23 | throw new Error(`Service '${constructor.name}' wasn't registered yet`); 24 | } 25 | 26 | return service as T; 27 | } 28 | 29 | public dispose() { 30 | if (this.disposed) { 31 | return; 32 | } 33 | 34 | this.services.clear(); 35 | ServiceLocator.instance = null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/feedback-sentiment-analysis/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: "customer_feedback_dataset_api", 7 | description: 8 | "Returns an array of individual customer‑feedback text snippets for the requested dataset ID.", 9 | toolInput: '{"datasetId":""}', 10 | }, 11 | { 12 | toolName: "sentiment_analysis_api", 13 | description: 14 | "Performs sentiment analysis on an array of text snippets and returns a score or label (positive / neutral / negative) for each, optionally with confidence values.", 15 | toolInput: 16 | '{"texts":[""],"outputFormat":"label|score","language":""}', 17 | }, 18 | ] as const satisfies AgentAvailableTool[]; 19 | 20 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 21 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/agent-config.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigTiny } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/dto.js"; 2 | 3 | import toolsFixtures from "./tools.js"; 4 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 5 | import { addAgentConfigMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 6 | type ToolName = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | agentType: `TBD`, 11 | description: `TBD`, 12 | instructions: `TBD`, 13 | tools: [] as const satisfies ToolName[], 14 | }, 15 | { 16 | agentType: `TBD`, 17 | description: `TBD`, 18 | instructions: `TBD`, 19 | tools: [] as const satisfies ToolName[], 20 | }, 21 | { 22 | agentType: `TBD`, 23 | description: `TBD`, 24 | instructions: `TBD`, 25 | tools: [] as const satisfies ToolName[], 26 | }, 27 | ] as const satisfies AgentConfigTiny[]; 28 | 29 | export default createFixtures( 30 | addAgentConfigMissingAttrs(ENTRIES), 31 | ({ agentType }) => agentType, 32 | ); 33 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/agent-config.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigTiny } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addAgentConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | 5 | import toolsFixtures from "./tools.js"; 6 | type ToolName = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | agentType: `TBD`, 11 | description: `TBD`, 12 | instructions: `TBD`, 13 | tools: [] as const satisfies ToolName[], 14 | }, 15 | { 16 | agentType: `TBD`, 17 | description: `TBD`, 18 | instructions: `TBD`, 19 | tools: [] as const satisfies ToolName[], 20 | }, 21 | { 22 | agentType: `TBD`, 23 | description: `TBD`, 24 | instructions: `TBD`, 25 | tools: [] as const satisfies ToolName[], 26 | }, 27 | ] as const satisfies AgentConfigTiny[]; 28 | 29 | export default createFixtures( 30 | addAgentConfigMissingAttrs(ENTRIES), 31 | ({ agentType }) => agentType, 32 | ); 33 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import agentConfigFixtures from "./agent-config.js"; 3 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 4 | import { addTaskConfigMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD`, 10 | agentType: `TBD` satisfies AgentType, 11 | description: `TBD`, 12 | taskConfigInput: `TBD`, 13 | }, 14 | { 15 | taskType: `TBD`, 16 | agentType: `TBD` satisfies AgentType, 17 | description: `TBD`, 18 | taskConfigInput: `TBD`, 19 | }, 20 | { 21 | taskType: `TBD`, 22 | agentType: `TBD` satisfies AgentType, 23 | description: `TBD`, 24 | taskConfigInput: `TBD`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/agent-config.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigTiny } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addAgentConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | 5 | import toolsFixtures from "./tools.js"; 6 | type ToolName = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | agentType: `TBD`, 11 | description: `TBD`, 12 | instructions: `TBD`, 13 | tools: [] as const satisfies ToolName[], 14 | }, 15 | { 16 | agentType: `TBD`, 17 | description: `TBD`, 18 | instructions: `TBD`, 19 | tools: [] as const satisfies ToolName[], 20 | }, 21 | { 22 | agentType: `TBD`, 23 | description: `TBD`, 24 | instructions: `TBD`, 25 | tools: [] as const satisfies ToolName[], 26 | }, 27 | ] as const satisfies AgentConfigTiny[]; 28 | 29 | export default createFixtures( 30 | addAgentConfigMissingAttrs(ENTRIES), 31 | ({ agentType }) => agentType, 32 | ); 33 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD`, 10 | agentType: `TBD` satisfies AgentType, 11 | description: `TBD`, 12 | taskConfigInput: `TBD`, 13 | }, 14 | { 15 | taskType: `TBD`, 16 | agentType: `TBD` satisfies AgentType, 17 | description: `TBD`, 18 | taskConfigInput: `TBD`, 19 | }, 20 | { 21 | taskType: `TBD`, 22 | agentType: `TBD` satisfies AgentType, 23 | description: `TBD`, 24 | taskConfigInput: `TBD`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/utils/text.ts: -------------------------------------------------------------------------------- 1 | export function truncateText(text: string, maxLength: number): string { 2 | if (text.length <= maxLength) { 3 | return text; 4 | } 5 | return text.substring(0, maxLength - 3) + "..."; 6 | } 7 | 8 | export function textSplitter( 9 | text: string, 10 | splitters: string[], 11 | backwards = false, 12 | ): string[] { 13 | if (splitters.length === 0) { 14 | return [text]; 15 | } 16 | 17 | const parts: string[] = []; 18 | let rest = text; 19 | 20 | for (const splitter of splitters) { 21 | const idx = backwards ? rest.lastIndexOf(splitter) : rest.indexOf(splitter); 22 | 23 | if (idx === -1) { 24 | throw new Error( 25 | `Splitter \`${splitter}\` not found in text \`${text}\` after \`${text.substring( 26 | 0, 27 | text.indexOf(rest), 28 | )}\``, 29 | ); 30 | } 31 | 32 | const chunk = rest.slice(0, idx); 33 | if (chunk.length) { 34 | parts.push(chunk); 35 | } 36 | 37 | rest = rest.slice(idx + splitter.length); 38 | } 39 | 40 | if (rest.length) { 41 | parts.push(rest); 42 | } 43 | 44 | return parts; 45 | } 46 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD`, 10 | agentType: `TBD` satisfies AgentType, 11 | description: `TBD`, 12 | taskConfigInput: `TBD`, 13 | }, 14 | { 15 | taskType: `TBD`, 16 | agentType: `TBD` satisfies AgentType, 17 | description: `TBD`, 18 | taskConfigInput: `TBD`, 19 | }, 20 | { 21 | taskType: `TBD`, 22 | agentType: `TBD` satisfies AgentType, 23 | description: `TBD`, 24 | taskConfigInput: `TBD`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/feedback-sentiment-analysis/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `load_customer_feedback_dataset` satisfies TaskType, 10 | taskRunInput: `{"datasetId": "cust-feedback-2025-06"}`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: `perform_sentiment_analysis` satisfies TaskType, 15 | taskRunInput: ``, 16 | taskRunNum: 1, 17 | }, 18 | { 19 | taskType: `aggregate_sentiment_scores` satisfies TaskType, 20 | taskRunInput: ``, 21 | taskRunNum: 1, 22 | }, 23 | ] as const satisfies TaskRunMinimal[]; 24 | 25 | export default createFixtures( 26 | addTaskRunMissingAttrs(ENTRIES), 27 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 28 | ); 29 | -------------------------------------------------------------------------------- /src/utils/time.ts: -------------------------------------------------------------------------------- 1 | import { DateString } from "@/base/dto.js"; 2 | 3 | export function formatDuration(ms: number): string { 4 | const seconds = Math.floor(ms / 1000); 5 | const minutes = Math.floor(seconds / 60); 6 | const hours = Math.floor(minutes / 60); 7 | const days = Math.floor(hours / 24); 8 | 9 | if (days > 0) { 10 | return `${days}d ${hours % 24}h`; 11 | } 12 | if (hours > 0) { 13 | return `${hours}h ${minutes % 60}m`; 14 | } 15 | if (minutes > 0) { 16 | return `${minutes}m ${seconds % 60}s`; 17 | } 18 | return `${seconds}s`; 19 | } 20 | 21 | export const sortByDateString = 22 | (direction: "asc" | "desc" = "asc") => 23 | (a: DateString, b: DateString) => 24 | (direction === "asc" ? 1 : -1) * 25 | (new Date(a).getTime() - new Date(b).getTime()); 26 | 27 | export const sortByObjectDateStringProperty = 28 | >( 29 | property: keyof T, 30 | direction: "asc" | "desc" = "asc", 31 | ) => 32 | (a: T, b: T) => { 33 | const dateComparer = sortByDateString(direction); 34 | return dateComparer(a[property] as DateString, b[property] as DateString); 35 | }; 36 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/templates/example.ts: -------------------------------------------------------------------------------- 1 | import { sectionTemplate, TitleTemplateInput } from "./body.js"; 2 | 3 | export interface ExamplePartInput { 4 | role: string; 5 | content: string; 6 | } 7 | 8 | export interface ExampleTemplateInput { 9 | title: TitleTemplateInput; 10 | position: number; 11 | subtitle?: string; 12 | parts: ExamplePartInput[]; 13 | } 14 | 15 | export class ExampleTemplateBuilder { 16 | private output: string; 17 | 18 | private constructor() { 19 | this.output = ""; 20 | } 21 | 22 | static new() { 23 | return new ExampleTemplateBuilder(); 24 | } 25 | 26 | title(input: TitleTemplateInput & { subtitle?: string; position?: number }) { 27 | const titleValue = `Example${input.position !== null ? `[${input.position}]` : ""}: ${input.text}${input.subtitle ? ` - ${input.subtitle}` : ""}`; 28 | this.output = sectionTemplate({ title: { ...input, text: titleValue } }); 29 | } 30 | 31 | part(input: ExamplePartInput) { 32 | const { role, content } = input; 33 | this.output += `**${role}:**\n${content}\n`; 34 | } 35 | 36 | build() { 37 | return this.output; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigSchema } from "@/tasks/manager/dto.js"; 2 | import { z } from "zod"; 3 | import { ResourcesSchema } from "../../helpers/resources/dto.js"; 4 | import { TaskStepSchema } from "../../helpers/task-step/dto.js"; 5 | 6 | export const TaskConfigMinimalSchema = TaskConfigSchema.pick({ 7 | taskType: true, 8 | agentType: true, 9 | taskConfigInput: true, 10 | description: true, 11 | }); 12 | export type TaskConfigMinimal = z.infer; 13 | 14 | export const TaskConfigInitializerInputSchema = z.object({ 15 | resources: ResourcesSchema, 16 | previousSteps: z.array(TaskStepSchema), 17 | taskStep: TaskStepSchema, 18 | actingAgentId: z.string(), 19 | }); 20 | export type TaskConfigInitializerInput = z.infer< 21 | typeof TaskConfigInitializerInputSchema 22 | >; 23 | 24 | export const TaskConfigInitializerOutputSchema = z.object({ 25 | resources: ResourcesSchema, 26 | taskStep: TaskStepSchema, 27 | }); 28 | export type TaskConfigInitializerOutput = z.infer< 29 | typeof TaskConfigInitializerOutputSchema 30 | >; 31 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/task-step/helpers/assert.ts: -------------------------------------------------------------------------------- 1 | import { TaskStepAssignedResourceEnum, TaskStep } from "../dto.js"; 2 | import { TaskStepResourceAssignError } from "../task-step-mapper.js"; 3 | 4 | export function assertTaskStepResourceType< 5 | T extends TaskStepAssignedResourceEnum, 6 | >( 7 | taskStep: TaskStep, 8 | expectedType: T | T[], 9 | ): asserts taskStep is TaskStep & { 10 | resource: Extract; 11 | } { 12 | const expectedTypes = Array.isArray(expectedType) 13 | ? expectedType 14 | : [expectedType]; 15 | 16 | if (!expectedTypes.includes(taskStep.resource.type as T)) { 17 | throw new Error( 18 | `Expected task step to have resource type "${expectedTypes.join( 19 | `" or "`, 20 | )}", but got "${taskStep.resource.type}".`, 21 | ); 22 | } 23 | } 24 | 25 | export function assertTaskSteps( 26 | input: (TaskStep | TaskStepResourceAssignError)[], 27 | ): asserts input is TaskStep[] { 28 | for (const it of input) { 29 | if (it instanceof TaskStepResourceAssignError) { 30 | throw new Error(`Task step resource assignment error: ${it.message}`); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { prompt } from "./prompt.js"; 3 | import { resolve } from "path"; 4 | import { readFileSync } from "fs"; 5 | import boston_trip_fixtures from "@/agents/supervisor/workflow/fixtures/__test__/boston-trip/index.js"; 6 | import { unwrapTaskStepWithTaskRun } from "@/agents/supervisor/workflow/fixtures/helpers/unwrap-task-step.js"; 7 | 8 | describe(`Prompt`, () => { 9 | it(`Sample`, () => { 10 | const fixtures = boston_trip_fixtures; 11 | const stepNo = 1; 12 | const stepIndex = stepNo - 1; // Adjust for zero-based index 13 | const p = prompt({ 14 | resources: { 15 | tools: [], //fixtures.tools.values, 16 | agents: [], //fixtures.agents.values.slice(0, stepIndex), 17 | tasks: [], //fixtures.tasks.values.slice(0, stepIndex), 18 | taskRuns: [], 19 | }, 20 | previousSteps: fixtures.taskSteps.values 21 | .slice(0, stepIndex) 22 | .map(unwrapTaskStepWithTaskRun), 23 | }); 24 | 25 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/base/retry/retry.ts: -------------------------------------------------------------------------------- 1 | import { Fn, FnErrorResult, RetryOptions, RetryResult } from "./dto.js"; 2 | 3 | export async function retry( 4 | fn: Fn, 5 | options?: RetryOptions, 6 | ): Promise> { 7 | const { maxRetries, delay } = Object.assign( 8 | { maxRetries: 3, delay: 0 }, 9 | options, 10 | ); 11 | 12 | const maxAttempts = maxRetries + 1; 13 | const attempts: FnErrorResult[] = []; 14 | let payload: P | undefined; 15 | for (let i = 0; i < maxAttempts; i++) { 16 | const attemptNo = i + 1; 17 | const { result, payload: p } = await fn( 18 | attempts.at(-1), 19 | attemptNo, 20 | payload, 21 | ); 22 | if (result.type === "SUCCESS") { 23 | return { ...result, attempts }; 24 | } 25 | 26 | if (result.type === "ERROR" && result.escalation) { 27 | return { ...result, attempts }; 28 | } 29 | 30 | attempts.push(result); 31 | payload = p; 32 | 33 | if (i < maxRetries) { 34 | await new Promise((resolve) => setTimeout(resolve, delay)); 35 | } 36 | } 37 | 38 | return { 39 | type: "ERROR", 40 | explanation: `Failed after ${maxAttempts} attempts`, 41 | attempts, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/poetry-song-analysis/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | // { 10 | // taskType: "search_round_trip_flights", 11 | // agentType: "flight_options_finder", 12 | // description: 13 | // "Task to search for round-trip flights between a specified origin and destination with given preferences, including departure and return dates and airline preferences.", 14 | // taskConfigInput: `{"origin":"","destination":"","departureDate":"","returnDate":"","airlinePreferences":""}`, 15 | // }, 16 | ] as const satisfies (TaskConfigMinimal & { agentType: AgentType })[]; 17 | 18 | export default createFixtures( 19 | addTaskConfigMissingAttrs(ENTRIES), 20 | ({ taskType }) => taskType, 21 | ); 22 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/asteroid-mining-feasibility/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | // Asteroid Analysis Tools (each essential but incomplete) 6 | { 7 | toolName: "spectral_composition_analyzer_api", 8 | description: 9 | "Analyzes asteroid mineral composition using spectroscopic data. Provides raw element percentages and mineral identification but cannot assess mining difficulty or economic viability.", 10 | toolInput: '{"asteroid_id":"","analysis_depth":"surface|deep"}', 11 | }, 12 | { 13 | toolName: "orbital_mechanics_calculator_api", 14 | description: 15 | "Calculates orbital parameters, approach windows, and delta-v requirements for asteroid missions. Uses asteroid ID to compute trajectory data and cross-references with mineral composition for mission planning.", 16 | toolInput: '{"asteroid_id":"","mineral_composition":""}', 17 | }, 18 | ] as const satisfies AgentAvailableTool[]; 19 | 20 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 21 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/asteroid-mining-feasibility/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `analyze_asteroid_mineral_composition` satisfies TaskType, 10 | taskRunInput: `{ "asteroid_id": "433-Eros", "analysis_depth": "deep" }`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: 15 | `cross_reference_mineral_composition_with_orbital_mechanics` satisfies TaskType, 16 | taskRunInput: `{ "asteroid_id": "433-Eros" }`, 17 | taskRunNum: 1, 18 | }, 19 | { 20 | taskType: `compile_mining_viability_report` satisfies TaskType, 21 | taskRunInput: ``, 22 | taskRunNum: 1, 23 | }, 24 | ] as const satisfies TaskRunMinimal[]; 25 | 26 | export default createFixtures( 27 | addTaskRunMissingAttrs(ENTRIES), 28 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 29 | ); 30 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.ts: -------------------------------------------------------------------------------- 1 | import { AgentIdValueSchema } from "@/agents/registry/dto.js"; 2 | import { 3 | AutomaticTaskRunSchema, 4 | TaskRunIdValueSchema, 5 | } from "@/tasks/manager/dto.js"; 6 | import { z } from "zod"; 7 | import { ResourcesSchema } from "../helpers/resources/dto.js"; 8 | import { TaskStepSchema } from "../helpers/task-step/dto.js"; 9 | 10 | export const TaskRunInputSchema = AutomaticTaskRunSchema.pick({ 11 | taskType: true, 12 | taskRunInput: true, 13 | taskRunNum: true, 14 | }); 15 | export type TaskRunMinimal = z.infer; 16 | 17 | export const TaskRunInitializerInputSchema = z.object({ 18 | resources: ResourcesSchema, 19 | previousSteps: z.array(TaskStepSchema), 20 | taskStep: TaskStepSchema, 21 | actingAgentId: AgentIdValueSchema, 22 | originTaskRunId: TaskRunIdValueSchema, 23 | }); 24 | export type TaskRunInitializerInput = z.infer< 25 | typeof TaskRunInitializerInputSchema 26 | >; 27 | 28 | export const TaskRunInitializerOutputSchema = z.object({ 29 | resources: ResourcesSchema, 30 | taskStep: TaskStepSchema, 31 | }); 32 | export type TaskRunInitializerOutput = z.infer< 33 | typeof TaskRunInitializerOutputSchema 34 | >; 35 | -------------------------------------------------------------------------------- /src/agents/base/agent-factory.ts: -------------------------------------------------------------------------------- 1 | import { BaseToolsFactory } from "@/base/tools-factory.js"; 2 | import { Switches } from "@/runtime/factory.js"; 3 | import { AssistantMessage, ToolMessage } from "beeai-framework/backend/message"; 4 | import { AgentKindEnum } from "../registry/dto.js"; 5 | import { Logger } from "beeai-framework"; 6 | import { TaskRunIdValue } from "@/tasks/manager/dto.js"; 7 | 8 | export interface CreateAgentInput { 9 | agentKind: AgentKindEnum; 10 | agentType: string; 11 | agentId: string; 12 | instructions: string; 13 | description: string; 14 | tools: string[]; 15 | } 16 | 17 | export abstract class BaseAgentFactory { 18 | protected logger: Logger; 19 | constructor(logger: Logger) { 20 | this.logger = logger.child({ 21 | name: this.constructor.name, 22 | }); 23 | } 24 | abstract createAgent( 25 | input: CreateAgentInput, 26 | toolsFactory: BaseToolsFactory, 27 | switches?: Switches, 28 | ): TAgent; 29 | abstract runAgent( 30 | agent: TAgent, 31 | prompt: string, 32 | onUpdate: (key: string, value: string) => void, 33 | signal: AbortSignal, 34 | taskRunId: TaskRunIdValue, 35 | addToMemory?: (AssistantMessage | ToolMessage)[], 36 | ): Promise; 37 | } 38 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/request-handler/protocol.ts: -------------------------------------------------------------------------------- 1 | import * as laml from "@/laml/index.js"; 2 | 3 | export const protocol = laml.ProtocolBuilder.new() 4 | .text({ 5 | name: "RESPONSE_CHOICE_EXPLANATION", 6 | description: 7 | "Brief explanation of *why* you selected the given RESPONSE_TYPE", 8 | }) 9 | .constant({ 10 | name: "RESPONSE_TYPE", 11 | values: ["DIRECT_ANSWER", "CLARIFICATION", "COMPOSE_WORKFLOW"] as const, 12 | description: 13 | "Valid values: DIRECT_ANSWER | CLARIFICATION | COMPOSE_WORKFLOW", 14 | }) 15 | .comment({ 16 | comment: 17 | "Follow by one of the possible responses format based on the chosen response type", 18 | }) 19 | .text({ 20 | name: "RESPONSE_DIRECT_ANSWER", 21 | isOptional: true, 22 | description: "Answer to the user", 23 | }) 24 | .text({ 25 | name: "RESPONSE_CLARIFICATION", 26 | isOptional: true, 27 | description: "Prompt the user for missing or clearer input", 28 | }) 29 | .text({ 30 | name: "RESPONSE_COMPOSE_WORKFLOW", 31 | isOptional: true, 32 | description: 33 | "A structured object captures the interpreted intent, goals, parameters, and expected deliverables of the user’s task request.", 34 | }) 35 | .build(); 36 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 © BeeAI a Series of LF Projects, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // @ts-check 18 | 19 | import eslint from "@eslint/js"; 20 | import tseslint from "typescript-eslint"; 21 | import prettierConfig from "eslint-config-prettier"; 22 | 23 | export default tseslint.config( 24 | { 25 | ignores: ["node_modules/**", "dist/**", "package-lock.json"], 26 | }, 27 | eslint.configs.recommended, 28 | ...tseslint.configs.stylistic, 29 | prettierConfig, 30 | { 31 | rules: { 32 | "curly": ["error", "all"], 33 | "no-unused-vars": "off", 34 | "@typescript-eslint/no-unused-vars": ["error"], 35 | }, 36 | }, 37 | ); 38 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | # Tools 2 | SEARCH_TOOL="tavily" # tavily/duckduckgo 3 | TAVILY_API_KEY="" 4 | 5 | # LLM Provider (openai/ollama/watsonx) 6 | LLM_BACKEND="openai" # Fallback backend for both agents. If both agent‑specific variables below are set, this fallback is optional. 7 | # LLM_BACKEND_SUPERVISOR="openai" # Backend for the supervisor agent 8 | # LLM_BACKEND_OPERATOR="ollama" # Backend for the operator agent 9 | 10 | ## OpenAI 11 | OPENAI_API_KEY="" 12 | OPENAI_MODEL_SUPERVISOR="gpt-4o-mini" 13 | OPENAI_MODEL_OPERATOR="gpt-4o-mini" 14 | 15 | ## Ollama 16 | OLLAMA_BASE_URL="http://0.0.0.0:11434/api" 17 | OLLAMA_MODEL_SUPERVISOR="deepseek-r1:8b" 18 | OLLAMA_MODEL_OPERATOR="deepseek-r1:8b" 19 | 20 | ## Watson X 21 | WATSONX_API_KEY="" 22 | WATSONX_BASE_URL="" 23 | WATSONX_PROJECT_ID="" 24 | WATSONX_CHAT_MODEL_SUPERVISOR="ibm/granite-3-3-8b-instruct" 25 | WATSONX_CHAT_MODEL_OPERATOR="ibm/granite-3-3-8b-instruct" 26 | 27 | # Framework related 28 | BEE_FRAMEWORK_LOG_PRETTY=false 29 | BEE_FRAMEWORK_LOG_LEVEL="debug" 30 | BEE_FRAMEWORK_LOG_SINGLE_LINE="false" 31 | 32 | # Development only 33 | DEV_SUPERVISOR_WORKFLOW_ENABLED=true 34 | DEV_SUPERVISOR_WORKFLOW_DISABLE_EXAMPLES=false -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/medieval-charter-digitisation/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: "hires_scan_api", 7 | description: 8 | "Produces 600 dpi TIFFs from book-cradle scanner, returning URLs & metadata.", 9 | toolInput: '{"documentIds":[""],"resolutionDPI":}', 10 | }, 11 | { 12 | toolName: "ocr_latin_script_api", 13 | description: 14 | "OCR tuned for Gothic/Caroline Latin; returns text + confidence.", 15 | toolInput: '{"imageUrl":"","languageHint":"lat"}', 16 | }, 17 | { 18 | toolName: "language_detect_api", 19 | description: 20 | "Detects language(s) in text snippets; returns ISO code & confidence.", 21 | toolInput: '{"text":""}', 22 | }, 23 | { 24 | toolName: "vector_store_ingest_api", 25 | description: "Chunks text, embeds, and stores with provenance tags.", 26 | toolInput: 27 | '{"documentId":"","text":"","chunkSize":}', 28 | }, 29 | ] as const satisfies AgentAvailableTool[]; 30 | 31 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-run-initializer/protocol.ts: -------------------------------------------------------------------------------- 1 | import * as laml from "@/laml/index.js"; 2 | 3 | export const protocol = laml.ProtocolBuilder.new() 4 | .text({ 5 | name: "RESPONSE_CHOICE_EXPLANATION", 6 | description: 7 | "Brief explanation of *why* you selected the given RESPONSE_TYPE", 8 | }) 9 | .constant({ 10 | name: "RESPONSE_TYPE", 11 | values: ["CREATE_TASK_RUN", "TASK_RUN_UNAVAILABLE"] as const, 12 | description: "Valid values: CREATE_TASK_RUN | TASK_RUN_UNAVAILABLE", 13 | }) 14 | .comment({ 15 | comment: 16 | "Follow by one of the possible responses format based on the chosen response type", 17 | }) 18 | .object({ 19 | name: "RESPONSE_CREATE_TASK_RUN", 20 | isOptional: true, 21 | attributes: laml.ProtocolBuilder.new().text({ 22 | name: "task_run_input", 23 | description: 24 | "Task inputs specific for the run. ⚠️ Your task_run_input must exclude any input values that are marked as [from Step X]", 25 | }), 26 | }) 27 | .object({ 28 | name: "RESPONSE_TASK_RUN_UNAVAILABLE", 29 | isOptional: true, 30 | attributes: laml.ProtocolBuilder.new().text({ 31 | name: "explanation", 32 | description: "Brief reason why you are unable to create a new task run", 33 | }), 34 | }) 35 | .build(); 36 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import taskConfigFixtures from "./task-config.js"; 3 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 4 | import { addTaskRunMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD` satisfies TaskType, 10 | taskRunInput: `TBD`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: `TBD` satisfies TaskType, 15 | taskRunInput: `TBD`, 16 | taskRunNum: 2, 17 | }, 18 | { 19 | taskType: `TBD` satisfies TaskType, 20 | taskRunInput: `TBD`, 21 | taskRunNum: 3, 22 | }, 23 | { 24 | taskType: `TBD` satisfies TaskType, 25 | taskRunInput: `TBD`, 26 | taskRunNum: 4, 27 | }, 28 | { 29 | taskType: `TBD` satisfies TaskType, 30 | taskRunInput: `TBD`, 31 | taskRunNum: 1, 32 | }, 33 | { 34 | taskType: `TBD` satisfies TaskType, 35 | taskRunInput: `TBD`, 36 | taskRunNum: 1, 37 | }, 38 | ] as const satisfies TaskRunMinimal[]; 39 | 40 | export default createFixtures( 41 | addTaskRunMissingAttrs(ENTRIES), 42 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 43 | ); 44 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/config.ts: -------------------------------------------------------------------------------- 1 | import { clone } from "remeda"; 2 | import * as st from "../../config.js"; 3 | import { UIColors } from "@/ui/colors.js"; 4 | import { getBorderedBoxStyle } from "../config.js"; 5 | 6 | export function getWorkflowPopupStyle(active = false) { 7 | return getBorderedBoxStyle(active); 8 | } 9 | 10 | export function getRunListStyle(active = false) { 11 | return getBorderedBoxStyle(active, { 12 | scrollbar: getRunListScrollbarStyle(), 13 | }); 14 | } 15 | 16 | export function getRunListScrollbarStyle() { 17 | return clone(st.UIConfig.scrollbar); 18 | } 19 | 20 | export function getPlayPauseButtonContent(isPlay: boolean) { 21 | return isPlay ? "▶ Play" : "⏸ Pause"; 22 | } 23 | 24 | export function getPlayPauseButtonStyle(isPlay: boolean, disabled: boolean) { 25 | return { 26 | align: "center" as any, 27 | valign: "middle" as any, 28 | style: { 29 | fg: disabled ? UIColors.gray.cool_gray : UIColors.white.white, 30 | bg: disabled 31 | ? UIColors.gray.gray 32 | : isPlay 33 | ? UIColors.blue.blue 34 | : UIColors.red.dark_red, 35 | focus: { 36 | bg: disabled 37 | ? UIColors.gray.cool_gray 38 | : isPlay 39 | ? UIColors.blue.electric_blue 40 | : UIColors.red.cardinal, 41 | }, 42 | }, 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD` satisfies TaskType, 10 | taskRunInput: `TBD`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: `TBD` satisfies TaskType, 15 | taskRunInput: `TBD`, 16 | taskRunNum: 2, 17 | }, 18 | { 19 | taskType: `TBD` satisfies TaskType, 20 | taskRunInput: `TBD`, 21 | taskRunNum: 3, 22 | }, 23 | { 24 | taskType: `TBD` satisfies TaskType, 25 | taskRunInput: `TBD`, 26 | taskRunNum: 4, 27 | }, 28 | { 29 | taskType: `TBD` satisfies TaskType, 30 | taskRunInput: `TBD`, 31 | taskRunNum: 1, 32 | }, 33 | { 34 | taskType: `TBD` satisfies TaskType, 35 | taskRunInput: `TBD`, 36 | taskRunNum: 1, 37 | }, 38 | ] as const satisfies TaskRunMinimal[]; 39 | 40 | export default createFixtures( 41 | addTaskRunMissingAttrs(ENTRIES), 42 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 43 | ); 44 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `TBD` satisfies TaskType, 10 | taskRunInput: `TBD`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: `TBD` satisfies TaskType, 15 | taskRunInput: `TBD`, 16 | taskRunNum: 2, 17 | }, 18 | { 19 | taskType: `TBD` satisfies TaskType, 20 | taskRunInput: `TBD`, 21 | taskRunNum: 3, 22 | }, 23 | { 24 | taskType: `TBD` satisfies TaskType, 25 | taskRunInput: `TBD`, 26 | taskRunNum: 4, 27 | }, 28 | { 29 | taskType: `TBD` satisfies TaskType, 30 | taskRunInput: `TBD`, 31 | taskRunNum: 1, 32 | }, 33 | { 34 | taskType: `TBD` satisfies TaskType, 35 | taskRunInput: `TBD`, 36 | taskRunNum: 1, 37 | }, 38 | ] as const satisfies TaskRunMinimal[]; 39 | 40 | export default createFixtures( 41 | addTaskRunMissingAttrs(ENTRIES), 42 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 43 | ); 44 | -------------------------------------------------------------------------------- /src/utils/file.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "beeai-framework"; 2 | import fs from "fs"; 3 | import { join, normalize, relative } from "path"; 4 | 5 | export function validatePath(parentDir: string, targetPath: string): string { 6 | // Normalize paths to remove any '..' or '.' segments and resolve relative paths 7 | const normalizedParent = normalize(parentDir); 8 | const normalizedTarget = normalize(join(normalizedParent, targetPath)); 9 | 10 | // Get relative path to check if it stays within parent 11 | const relativePath = relative(normalizedParent, normalizedTarget); 12 | 13 | if (relativePath.startsWith("..") || targetPath.startsWith("/")) { 14 | throw new Error( 15 | `Path traversal attempt detected for path: "${targetPath}"`, 16 | ); 17 | } 18 | 19 | // Return the clean relative path 20 | return relativePath; 21 | } 22 | 23 | export function ensureDirectoryExistsSafe( 24 | baseDirPath: string, 25 | dirPath: string, 26 | logger?: Logger, 27 | ) { 28 | const validDirPath = validatePath(baseDirPath, dirPath); 29 | 30 | try { 31 | if (!fs.existsSync(validDirPath)) { 32 | fs.mkdirSync(validDirPath, { recursive: true }); 33 | logger?.info(`Created directory: ${dirPath}`); 34 | } 35 | return validDirPath; 36 | } catch (error) { 37 | logger?.error(`Error creating directory: ${dirPath}`); 38 | throw error; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/dto.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ResourcesSchema } from "../../helpers/resources/dto.js"; 3 | import { AgentConfigSchema } from "@/agents/registry/dto.js"; 4 | import { TaskStepSchema } from "../../helpers/task-step/dto.js"; 5 | 6 | export const AgentConfigTinyDraftSchema = AgentConfigSchema.pick({ 7 | agentType: true, 8 | tools: true, 9 | description: true, 10 | }); 11 | export type AgentConfigTinyDraft = z.infer; 12 | 13 | export const AgentConfigTinySchema = AgentConfigSchema.pick({ 14 | agentType: true, 15 | tools: true, 16 | description: true, 17 | instructions: true, 18 | }); 19 | export type AgentConfigTiny = z.infer; 20 | 21 | export const AgentConfigInitializerInputSchema = z.object({ 22 | resources: ResourcesSchema, 23 | previousSteps: z.array(TaskStepSchema), 24 | selectOnly: z.boolean().optional(), 25 | taskStep: TaskStepSchema, 26 | }); 27 | export type AgentConfigInitializerInput = z.infer< 28 | typeof AgentConfigInitializerInputSchema 29 | >; 30 | 31 | export const AgentConfigInitializerOutputSchema = z.object({ 32 | resources: ResourcesSchema, 33 | taskStep: TaskStepSchema, 34 | }); 35 | 36 | export type AgentConfigInitializerOutput = z.infer< 37 | typeof AgentConfigInitializerOutputSchema 38 | >; 39 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/micro-grid-load-balancing/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: "demand_forecast_api", 7 | description: "Predicts 15-min electricity demand per city block.", 8 | toolInput: 9 | '{"blockIds":[""],"startISO":"","periods":}', 10 | }, 11 | { 12 | toolName: "solar_battery_forecast_api", 13 | description: "Projects rooftop-solar output & battery state of charge.", 14 | toolInput: 15 | '{"siteIds":[""],"startISO":"","periods":}', 16 | }, 17 | { 18 | toolName: "grid_load_optimizer_api", 19 | description: 20 | "Optimises inverter set-points under cost & frequency constraints; returns control vectors & KPI report.", 21 | toolInput: 22 | '{"demandForecast":[/* … */],"supplyForecast":[/* … */],"objective":"cost","constraints":{"freqDeviationHz":0.2}}', 23 | }, 24 | { 25 | toolName: "dispatch_command_api", 26 | description: "Sends control vectors; returns acknowledgements.", 27 | toolInput: '{"controlVectors":[/* … */]}', 28 | }, 29 | ] as const satisfies AgentAvailableTool[]; 30 | 31 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 32 | -------------------------------------------------------------------------------- /src/laml/parser-output.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { ParserOutput } from "./parser-output.js"; 3 | 4 | describe("LAML Parser output", () => { 5 | describe("Set", () => { 6 | it("Top level", () => { 7 | const output = new ParserOutput(); 8 | output.set( 9 | ["RESPONSE_CHOICE_EXPLANATION"], 10 | "No existing agent can gather tweets on demand; a new config is required.", 11 | ); 12 | expect(output.result).toEqual({ 13 | RESPONSE_CHOICE_EXPLANATION: 14 | "No existing agent can gather tweets on demand; a new config is required.", 15 | }); 16 | }); 17 | 18 | it("Nested level; Existing object", () => { 19 | const output = new ParserOutput(); 20 | output.set(["RESPONSE_CREATE_AGENT_CONFIG"], {}); 21 | output.set(["RESPONSE_CREATE_AGENT_CONFIG", "agent_type"], "my_agent"); 22 | 23 | expect(output.result).toEqual({ 24 | RESPONSE_CREATE_AGENT_CONFIG: { 25 | agent_type: "my_agent", 26 | }, 27 | }); 28 | }); 29 | 30 | it("Nested level; Non-existing object", () => { 31 | const output = new ParserOutput(); 32 | output.set(["RESPONSE_CREATE_AGENT_CONFIG", "agent_type"], "my_agent"); 33 | 34 | expect(output.result).toEqual({ 35 | RESPONSE_CREATE_AGENT_CONFIG: { 36 | agent_type: "my_agent", 37 | }, 38 | }); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/micro-grid-load-balancing/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "forecast_electricity_demand" satisfies TaskType, 11 | taskRunInput: `{"blockIds": ["block-central-2", "block-north-7"], "start_time": "2025-06-05T18:00Z", "periods": "12"}`, 12 | taskRunNum: 1, 13 | }, 14 | { 15 | taskType: "forecast_solar_battery" satisfies TaskType, 16 | taskRunInput: `{"siteIds": ["site-solar-01", "site-battery-02"], "start_time": "2025-06-05T18:00Z", "periods": "12"}`, 17 | taskRunNum: 1, 18 | }, 19 | { 20 | taskType: "optimize_dispatch_schedule" satisfies TaskType, 21 | taskRunInput: `{ "constraint": { "freqDeviationHz": "0.2" } }`, 22 | taskRunNum: 1, 23 | }, 24 | { 25 | taskType: "send_control_vectors" satisfies TaskType, 26 | taskRunInput: ``, 27 | taskRunNum: 1, 28 | }, 29 | ] as const satisfies TaskRunMinimal[]; 30 | 31 | export default createFixtures( 32 | addTaskRunMissingAttrs(ENTRIES), 33 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 34 | ); 35 | -------------------------------------------------------------------------------- /src/ui/base/monitor.ts: -------------------------------------------------------------------------------- 1 | import blessed from "neo-blessed"; 2 | import { 3 | ControllableContainer, 4 | ControllableScreen, 5 | ControlsManager, 6 | } from "../controls/controls-manager.js"; 7 | import { Logger } from "beeai-framework"; 8 | 9 | export interface ParentInput { 10 | kind: "parent"; 11 | parent: ControllableContainer | ControllableScreen; 12 | controlsManager: ControlsManager; 13 | } 14 | 15 | export interface ScreenInput { 16 | kind: "screen"; 17 | title: string; 18 | } 19 | 20 | export abstract class ContainerComponent { 21 | protected logger: Logger; 22 | protected controlsManager: ControlsManager; 23 | protected screen: ControllableScreen; 24 | protected parent: ControllableContainer | ControllableScreen; 25 | 26 | constructor(arg: ParentInput | ScreenInput, logger: Logger) { 27 | this.logger = logger.child({ 28 | name: this.constructor.name, 29 | }); 30 | if (arg.kind === "screen") { 31 | this.controlsManager = new ControlsManager( 32 | blessed.screen({ 33 | smartCSR: true, 34 | title: arg.title, 35 | }), 36 | this.logger, 37 | ); 38 | this.screen = this.controlsManager.screen; 39 | this.parent = this.screen; 40 | } else { 41 | const { parent, controlsManager } = arg as ParentInput; 42 | this.controlsManager = controlsManager; 43 | this.screen = this.controlsManager.screen; 44 | this.parent = parent; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/tools/openf1/list-seasons.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { 4 | FormattedToolOutput, 5 | formatToolDescription, 6 | formatToolOutput, 7 | } from "../helpers/tool-format.js"; 8 | import { Season } from "./dto.js"; 9 | import { OpenF1Service } from "./service.js"; 10 | 11 | export type OutputObject = Season[]; 12 | 13 | export type ListSeasonsToolOutput = FormattedToolOutput; 14 | 15 | const example = [ 16 | { 17 | season_id: 2022, 18 | year: 2022, 19 | state: "completed", 20 | }, 21 | { 22 | season_id: 2023, 23 | year: 2023, 24 | state: "completed", 25 | }, 26 | ] satisfies OutputObject; 27 | 28 | export class ListSeasonsTool extends Tool { 29 | name = "f1_list_seasons"; 30 | description = formatToolDescription({ 31 | description: "Provides list of all available F1 seasons.", 32 | example, 33 | }); 34 | 35 | static { 36 | this.register(); 37 | } 38 | 39 | public readonly emitter: ToolEmitter, ListSeasonsToolOutput> = 40 | Emitter.root.child({ 41 | namespace: ["tool", "f1_list_seasons"], 42 | creator: this, 43 | }); 44 | 45 | inputSchema() { 46 | return {}; 47 | } 48 | 49 | protected async _run() { 50 | const output = await OpenF1Service.getInstance().listSeasons(); 51 | return formatToolOutput(output); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/utils/text.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { textSplitter } from "./text.js"; 3 | 4 | describe("TextUtils", () => { 5 | describe("textSplitter", () => { 6 | it("should split text by multiple delimiters", () => { 7 | const text = "Hello, world! How are you?"; 8 | const delimiters = [", ", "! ", "?"]; 9 | const result = textSplitter(text, delimiters); 10 | expect(result).toEqual(["Hello", "world", "How are you"]); 11 | }); 12 | 13 | it("should throw an error if a delimiter is not found", () => { 14 | const text = "Hello, world. How are you?"; 15 | const delimiters = [", ", "! ", "?"]; 16 | const result = () => textSplitter(text, delimiters); 17 | expect(result).toThrowError( 18 | "Splitter `! ` not found in text `Hello, world. How are you?` after `Hello, `", 19 | ); 20 | }); 21 | it("should return the original text if no delimiters are provided", () => { 22 | const text = "Hello, world! How are you?"; 23 | const delimiters: string[] = []; 24 | const result = textSplitter(text, delimiters); 25 | expect(result).toEqual([text]); 26 | }); 27 | it("should split text backward", () => { 28 | const text = "Hel[lo,] (world!) How (are)[you?]"; 29 | const delimiters: string[] = ["(", ")", "[", "]"]; 30 | const result = textSplitter(text, delimiters, true); 31 | expect(result).toEqual(["Hel[lo,] (world!) How ", "are", "you?"]); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/medieval-charter-digitisation/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "scan_documents_high_res" satisfies TaskType, 11 | taskRunInput: `{"document_ids": ["doc-latin-001", "doc-latin-002", "doc-latin-003", "doc-latin-004", "doc-latin-005"]}`, 12 | taskRunNum: 1, 13 | }, 14 | { 15 | taskType: "extract_text_from_images_latin_script" satisfies TaskType, 16 | taskRunInput: `{"language_hint": "lat"}`, 17 | taskRunNum: 1, 18 | }, 19 | { 20 | taskType: "verify_language_of_extracted_text" satisfies TaskType, 21 | taskRunInput: ``, 22 | taskRunNum: 1, 23 | }, 24 | { 25 | taskType: "load_verified_text_into_vector_search" satisfies TaskType, 26 | taskRunInput: `{ "document_ids": ["doc-latin-001", "doc-latin-002", "doc-latin-003", "doc-latin-004", "doc-latin-005"], "chunk_size": 1000 }`, 27 | taskRunNum: 1, 28 | }, 29 | ] as const satisfies TaskRunMinimal[]; 30 | 31 | export default createFixtures( 32 | addTaskRunMissingAttrs(ENTRIES), 33 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 34 | ); 35 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/agent-instructions-builder/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import boston_trip_fixtures from "@/agents/supervisor/workflow/fixtures/__test__/boston-trip/index.js"; 2 | import { 3 | unwrapTaskStepWithAgent, 4 | unwrapTaskStepWithToolsOrLLM, 5 | } from "@/agents/supervisor/workflow/fixtures/helpers/unwrap-task-step.js"; 6 | import { readFileSync } from "fs"; 7 | import { resolve } from "path"; 8 | import { describe, expect, it } from "vitest"; 9 | import { prompt } from "./prompt.js"; 10 | 11 | describe(`Prompt`, () => { 12 | it(`Sample`, () => { 13 | const fixtures = boston_trip_fixtures; 14 | const stepNo = 1; 15 | const stepIndex = stepNo - 1; // Adjust for zero-based index 16 | const p = prompt({ 17 | resources: { 18 | tools: fixtures.tools.values, 19 | agents: fixtures.agents.values.slice(0, stepIndex), 20 | tasks: fixtures.tasks.values.slice(0, stepIndex), 21 | taskRuns: fixtures.taskRuns.values.slice(0, stepIndex), 22 | }, 23 | previousSteps: fixtures.taskSteps.values 24 | .slice(0, stepIndex) 25 | .map(unwrapTaskStepWithAgent), 26 | taskStep: unwrapTaskStepWithToolsOrLLM(fixtures.taskSteps.at(stepIndex)), 27 | agentConfigDraft: unwrapTaskStepWithAgent( 28 | fixtures.taskSteps.at(stepIndex), 29 | ).resource.agent, 30 | }); 31 | 32 | expect(p).toEqual(readFileSync(resolve(__dirname, "prompt.txt"), "utf-8")); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/boston-trip/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | 3 | import taskConfigFixtures from "./task-config.js"; 4 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 5 | import { addTaskRunMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 6 | 7 | type TaskType = FixtureName; 8 | 9 | const ENTRIES = [ 10 | { 11 | taskType: "find_historical_sites" satisfies TaskType, 12 | taskRunInput: `{"location": "Back Bay"}`, 13 | taskRunNum: 1, 14 | }, 15 | { 16 | taskType: "search_sports_events" satisfies TaskType, 17 | taskRunInput: `{"location": "Boston", "duration": "3 days"}`, 18 | taskRunNum: 1, 19 | }, 20 | { 21 | taskType: "search_dining_recommendations" satisfies TaskType, 22 | taskRunInput: `{"location": "Boston", "dining_preferences": ["Italian", "Chinese", "French"]}`, 23 | taskRunNum: 1, 24 | }, 25 | { 26 | taskType: "compile_itinerary" satisfies TaskType, 27 | taskRunInput: ``, 28 | taskRunNum: 1, 29 | }, 30 | // { 31 | // taskType: "retrieve_field_metadata" satisfies TaskType, 32 | // taskRunInput: `{ "field_identifier": "South Field" }`, 33 | // taskRunNum: 1, 34 | // }, 35 | ] as const satisfies TaskRunMinimal[]; 36 | 37 | export default createFixtures( 38 | addTaskRunMissingAttrs(ENTRIES), 39 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 40 | ); 41 | -------------------------------------------------------------------------------- /src/utils/objects.ts: -------------------------------------------------------------------------------- 1 | type DeepPartial = { 2 | [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; 3 | }; 4 | 5 | /** 6 | * Recursively updates original object with values from update object 7 | * @param original The original object to update 8 | * @param update Partial object containing updates 9 | * @returns The updated original object 10 | */ 11 | export function updateDeepPartialObject( 12 | original: T, 13 | update?: DeepPartial, 14 | ): T { 15 | if (!update) { 16 | return original; 17 | } 18 | 19 | const keys = Object.keys(update) as (keyof T)[]; 20 | 21 | for (const key of keys) { 22 | const updateValue = update[key]; 23 | 24 | // Special case for Date objects 25 | if (updateValue instanceof Date) { 26 | original[key] = updateValue as T[keyof T]; 27 | } 28 | // Handle nested objects recursively 29 | else if ( 30 | updateValue !== null && 31 | typeof updateValue === "object" && 32 | !Array.isArray(updateValue) && 33 | typeof original[key] === "object" && 34 | !(original[key] instanceof Date) // Don't treat Date as a regular object 35 | ) { 36 | original[key] = { 37 | ...(original[key] as object), 38 | ...updateDeepPartialObject( 39 | original[key] as object, 40 | updateValue as DeepPartial, 41 | ), 42 | } as T[keyof T]; 43 | } else { 44 | original[key] = updateValue as T[keyof T]; 45 | } 46 | } 47 | 48 | return original; 49 | } 50 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2023", 4 | "module": "NodeNext", 5 | "rootDir": ".", 6 | "sourceRoot": ".", 7 | "baseUrl": ".", 8 | "moduleResolution": "NodeNext", 9 | "incremental": false, 10 | "moduleDetection": "auto", 11 | "types": [ 12 | "node", 13 | "neo-blessed", 14 | ], 15 | "declaration": true, 16 | "declarationMap": true, 17 | "sourceMap": true, 18 | "allowJs": true, 19 | "outDir": "./dist", 20 | "esModuleInterop": true, 21 | "experimentalDecorators": true, 22 | "emitDecoratorMetadata": true, 23 | "useUnknownInCatchVariables": false, 24 | "strict": true, 25 | "skipLibCheck": true, 26 | "strictNullChecks": true, 27 | "removeComments": false, 28 | "forceConsistentCasingInFileNames": true, 29 | "paths": { 30 | "@/*": [ 31 | "./src/*" 32 | ], 33 | "@agents/*": [ 34 | "./src/agents/*" 35 | ], 36 | "@tasks/*": [ 37 | "./src/tasks/*" 38 | ], 39 | "@workspaces/*": [ 40 | "./src/workspaces/*" 41 | ], 42 | "@ui/*": [ 43 | "./src/ui/*" 44 | ], 45 | "@runtime/*": [ 46 | "./src/runtime/*" 47 | ], 48 | "@test/*": [ 49 | "./tests/*" 50 | ] 51 | }, 52 | "typeRoots": [ 53 | "./node_modules/@types", 54 | "./types" 55 | ] 56 | }, 57 | "include": [ 58 | "src/**/*", 59 | "tests/**/*" 60 | ], 61 | "exclude": [ 62 | "examples" 63 | ] 64 | } -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/beekeeping-site-analysis/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: "analyze_flora_for_nectar_sources" satisfies TaskType, 10 | taskRunInput: `{"location": "Sunnybrook Farm"}`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: "analyze_flora_for_nectar_sources" satisfies TaskType, 15 | taskRunInput: `{"location": "Meadowland Reserve"}`, 16 | taskRunNum: 2, 17 | }, 18 | { 19 | taskType: `analyze_flora_for_butterfly_host_plants` satisfies TaskType, 20 | taskRunInput: `{"location": "Sunnybrook Farm"}`, 21 | taskRunNum: 1, 22 | }, 23 | { 24 | taskType: `analyze_flora_for_butterfly_host_plants` satisfies TaskType, 25 | taskRunInput: `{"location": "Meadowland Reserve"}`, 26 | taskRunNum: 2, 27 | }, 28 | { 29 | taskType: `compile_farming_suitability_report` satisfies TaskType, 30 | taskRunInput: ``, 31 | taskRunNum: 1, 32 | }, 33 | ] as const satisfies TaskRunMinimal[]; 34 | 35 | export default createFixtures( 36 | addTaskRunMissingAttrs(ENTRIES), 37 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 38 | ); 39 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/smart-farm-harvest-planner/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "retrieve_field_metadata" satisfies TaskType, 11 | taskRunInput: `{ "field_identifier": "South Field" }`, 12 | taskRunNum: 1, 13 | }, 14 | { 15 | taskType: "retrieve_equipment_ids" satisfies TaskType, 16 | taskRunInput: ``, 17 | taskRunNum: 1, 18 | }, 19 | { 20 | taskType: "retrieve_weather_forecast" satisfies TaskType, 21 | taskRunInput: ``, 22 | taskRunNum: 1, 23 | }, 24 | { 25 | taskType: "check_equipment_operational_status" satisfies TaskType, 26 | taskRunInput: ``, 27 | taskRunNum: 1, 28 | }, 29 | { 30 | taskType: "generate_harvest_schedule" satisfies TaskType, 31 | taskRunInput: ``, 32 | taskRunNum: 1, 33 | }, 34 | { 35 | taskType: "generate_harvest_timeline" satisfies TaskType, 36 | taskRunInput: ``, 37 | taskRunNum: 1, 38 | }, 39 | ] as const satisfies TaskRunMinimal[]; 40 | 41 | export default createFixtures( 42 | addTaskRunMissingAttrs(ENTRIES), 43 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 44 | ); 45 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/base/fixtures.ts: -------------------------------------------------------------------------------- 1 | export class Fixtures { 2 | private readonly entries: Map; 3 | 4 | constructor(entries: [T, U][]) { 5 | this.entries = new Map(entries); 6 | } 7 | 8 | get(name: Name): U { 9 | const entry = this.entries.get(name); 10 | if (!entry) { 11 | throw new Error(`No entry found for name: ${name}`); 12 | } 13 | return entry; 14 | } 15 | 16 | stepNo(name: T): number { 17 | const entry = this.entries.get(name); 18 | if (!entry) { 19 | throw new Error(`No entry found for name: ${name}`); 20 | } 21 | const stepNo = (entry as any).no; // Assuming U has a 'no' property 22 | if (typeof stepNo !== "number") { 23 | throw new Error(`Entry for name: ${name} does not have a 'no' property`); 24 | } 25 | return stepNo; 26 | } 27 | 28 | at(index: number): U { 29 | const entry = Array.from(this.entries.values())[index]; 30 | if (!entry) { 31 | throw new Error(`No entry found at index: ${index}`); 32 | } 33 | return entry; 34 | } 35 | 36 | get values(): U[] { 37 | return Array.from(this.entries.values()).map((entry) => entry); 38 | } 39 | } 40 | 41 | export function createFixtures( 42 | entries: U[], 43 | getName: (entry: U) => T, 44 | ) { 45 | return new Fixtures( 46 | entries.map((entry) => [getName(entry), entry] as [T, U]), 47 | ); 48 | } 49 | 50 | export type FixtureName = T extends Fixtures ? Name : never; 51 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/templates/chat-example.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ExampleTemplateInput, 3 | ExamplePartInput, 4 | ExampleTemplateBuilder, 5 | } from "./example.js"; 6 | import { sectionTemplate } from "./body.js"; 7 | 8 | export interface ChatExampleTemplateInput 9 | extends Omit { 10 | context?: string; 11 | messages: ExamplePartInput[]; 12 | } 13 | 14 | export class ChatExampleTemplateBuilder { 15 | private builder: ExampleTemplateBuilder; 16 | 17 | private constructor() { 18 | this.builder = ExampleTemplateBuilder.new(); 19 | } 20 | 21 | static new() { 22 | return new ChatExampleTemplateBuilder(); 23 | } 24 | 25 | title(...args: Parameters<(typeof this.builder)["title"]>) { 26 | this.builder.title(...args); 27 | return this; 28 | } 29 | 30 | context(content: string) { 31 | this.builder.part({ 32 | role: "Context", 33 | content: sectionTemplate({ 34 | content: content, 35 | delimiter: { start: true, end: true }, 36 | newLines: { 37 | start: 0, 38 | contentEnd: 0, 39 | end: 0, 40 | }, 41 | }), 42 | }); 43 | return this; 44 | } 45 | 46 | user(content: string) { 47 | this.builder.part({ 48 | role: "User", 49 | content, 50 | }); 51 | return this; 52 | } 53 | 54 | assistant(content: string) { 55 | this.builder.part({ 56 | role: "Assistant", 57 | content, 58 | }); 59 | return this; 60 | } 61 | 62 | build() { 63 | return this.builder.build(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/narrative-fusion/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "generate_short_story" satisfies TaskType, 11 | taskRunInput: `{"story_concept":"time travel"}`, 12 | taskRunNum: 1, 13 | }, 14 | { 15 | taskType: "generate_short_story" satisfies TaskType, 16 | taskRunInput: `{"story_concept":"bioluminescent fungi"}`, 17 | taskRunNum: 2, 18 | }, 19 | { 20 | taskType: "generate_short_story" satisfies TaskType, 21 | taskRunInput: `{"story_concept":"ancient desert rituals"}`, 22 | taskRunNum: 3, 23 | }, 24 | { 25 | taskType: "generate_short_story" satisfies TaskType, 26 | taskRunInput: `{"story_concept":"urban foxes"}`, 27 | taskRunNum: 4, 28 | }, 29 | { 30 | taskType: "merge_short_stories_into_screenplay_scene" satisfies TaskType, 31 | taskRunInput: ``, 32 | taskRunNum: 1, 33 | }, 34 | { 35 | taskType: "analyze_screenplay_scene_convergence" satisfies TaskType, 36 | taskRunInput: `TBD`, 37 | taskRunNum: 1, 38 | }, 39 | ] as const satisfies TaskRunMinimal[]; 40 | 41 | export default createFixtures( 42 | addTaskRunMissingAttrs(ENTRIES), 43 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 44 | ); 45 | -------------------------------------------------------------------------------- /examples/ui/shared/help-bar.ts: -------------------------------------------------------------------------------- 1 | import blessed from "neo-blessed"; 2 | import { Messages } from "../../../src/ui/chat-monitor/messages/messages.js"; 3 | import { MessageTypeEnum } from "../../../src/ui/chat-monitor/runtime-handler.js"; 4 | import { ControlsManager } from "../../../src/ui/controls/controls-manager"; 5 | import { HelpBar } from "../../../src/ui/shared/help-bar.js"; 6 | import testMessages from "../chat-monitor/messages/messages.json"; 7 | import { getLogger } from "../helpers/log.js"; 8 | 9 | const logger = getLogger(true); 10 | const screen = blessed.screen({ title: "Help Bar" }); 11 | const controlsManager = new ControlsManager(screen, logger); 12 | 13 | new HelpBar({ 14 | kind: "parent", 15 | parent: controlsManager.screen, 16 | controlsManager, 17 | }); 18 | 19 | function extractChatFilterValues(messages) { 20 | const messageTypeSet = new Set(); 21 | const roleSet = new Set(); 22 | 23 | for (const message of messages) { 24 | if (message.type) { 25 | messageTypeSet.add(message.type); 26 | } 27 | if (message.role) { 28 | roleSet.add(message.role); 29 | } 30 | } 31 | 32 | return { 33 | messageTypes: Array.from(messageTypeSet), 34 | roles: Array.from(roleSet), 35 | }; 36 | } 37 | 38 | const messages = new Messages( 39 | { 40 | parent: controlsManager.screen, 41 | controlsManager, 42 | getChatFilters: () => extractChatFilterValues(testMessages), 43 | }, 44 | logger, 45 | ); 46 | controlsManager.focus(messages.container.id); 47 | 48 | testMessages.forEach((message) => 49 | messages.addMessage( 50 | message.role, 51 | message.content, 52 | message.type as MessageTypeEnum, 53 | ), 54 | ); 55 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/feedback-sentiment-analysis/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `load_customer_feedback_dataset`, 10 | agentType: `customer_feedback_loader` satisfies AgentType, 11 | description: `Load the customer feedback dataset using the provided and return an array of feedback texts.`, 12 | taskConfigInput: `{"datasetId": ""}`, 13 | }, 14 | { 15 | taskType: `perform_sentiment_analysis`, 16 | agentType: `sentiment_analysis_agent` satisfies AgentType, 17 | description: `Perform sentiment analysis on the provided array of feedback texts and return sentiment scores for each text.`, 18 | taskConfigInput: `{"texts": ["", "..."]}`, 19 | }, 20 | { 21 | taskType: `aggregate_sentiment_scores`, 22 | agentType: `sentiment_aggregator` satisfies AgentType, 23 | description: `Aggregate the provided sentiment scores to generate a comprehensive sentiment report, summarizing key trends and insights.`, 24 | taskConfigInput: `{"sentimentScores": ["", "..."]}`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/base/workflow-compose-fixtures.ts: -------------------------------------------------------------------------------- 1 | import { RequestHandlerOutput } from "../../request-handler/dto.js"; 2 | import { Fixtures } from "./fixtures.js"; 3 | 4 | export interface ChoiceExplanations { 5 | requestHandler: string; 6 | problemDecomposer: string; 7 | steps: { 8 | no: number; 9 | agentConfig: string; 10 | taskConfig: string; 11 | taskRun: string; 12 | }[]; 13 | } 14 | 15 | export class WorkflowComposeFixture< 16 | TTools extends Fixtures = Fixtures, 17 | TTaskSteps extends Fixtures = Fixtures, 18 | TAgents extends Fixtures = Fixtures, 19 | TTasks extends Fixtures = Fixtures, 20 | TTaskRuns extends Fixtures = Fixtures, 21 | > { 22 | constructor( 23 | public readonly title: string, 24 | public readonly request: string, 25 | public readonly choiceExplanations: ChoiceExplanations, 26 | public readonly requestHandlerOutput: RequestHandlerOutput["response"], 27 | public readonly taskSteps: TTaskSteps, 28 | public readonly tools: TTools, 29 | public readonly agents: TAgents, 30 | public readonly tasks: TTasks, 31 | public readonly taskRuns: TTaskRuns, 32 | ) {} 33 | 34 | getChoiceExplanation( 35 | stepNo: number, 36 | type: "agentConfig" | "taskConfig" | "taskRun", 37 | ) { 38 | const explanation = this.choiceExplanations.steps.find( 39 | (step) => step.no === stepNo, 40 | )?.[type]; 41 | 42 | if (!explanation) { 43 | throw new Error( 44 | `No explanation found for step ${stepNo} and type ${type}`, 45 | ); 46 | } 47 | 48 | return explanation; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/task-step.ts: -------------------------------------------------------------------------------- 1 | import agentsFixtures from "./agent-config.js"; 2 | import toolsFixtures from "./tools.js"; 3 | import tasksFixtures from "./task-config.js"; 4 | import taskRunsFixtures from "./task-run.js"; 5 | import { TaskStepMapper } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/task-step-mapper.js"; 6 | import { FixtureName, createFixtures } from "../../base/fixtures.js"; 7 | import { 8 | createResourceFixtures, 9 | TaskStepWithVariousResource, 10 | } from "../../base/resource-fixtures.js"; 11 | 12 | type ToolName = FixtureName; 13 | 14 | const ENTRIES = [ 15 | { 16 | no: 1, 17 | step: `TBD`, 18 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 19 | resource: createResourceFixtures( 20 | { type: "tools", tools: ["TBD"] as ToolName[] }, 21 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 22 | { type: "task", task: tasksFixtures.get(`TBD`) }, 23 | { 24 | type: "task_run", 25 | taskRun: taskRunsFixtures.get("TBD_1"), 26 | }, 27 | ), 28 | }, 29 | { 30 | no: 2, 31 | step: `TBD`, 32 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 33 | resource: createResourceFixtures( 34 | { type: "llm" }, 35 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 36 | { type: "task", task: tasksFixtures.get(`TBD`) }, 37 | { 38 | type: "task_run", 39 | taskRun: taskRunsFixtures.get("TBD_1"), 40 | }, 41 | ), 42 | }, 43 | ] as const satisfies TaskStepWithVariousResource[]; 44 | 45 | const fixtures = createFixtures(ENTRIES, ({ step }) => step); 46 | export default fixtures; 47 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/task-step.ts: -------------------------------------------------------------------------------- 1 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 2 | import { 3 | createResourceFixtures, 4 | TaskStepWithVariousResource, 5 | } from "../../../base/resource-fixtures.js"; 6 | import agentsFixtures from "./agent-config.js"; 7 | import toolsFixtures from "./tools.js"; 8 | import tasksFixtures from "./task-config.js"; 9 | import taskRunsFixtures from "./task-run.js"; 10 | import { TaskStepMapper } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/task-step-mapper.js"; 11 | 12 | type ToolName = FixtureName; 13 | 14 | const ENTRIES = [ 15 | { 16 | no: 1, 17 | step: `TBD`, 18 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 19 | resource: createResourceFixtures( 20 | { type: "tools", tools: ["TBD"] as ToolName[] }, 21 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 22 | { type: "task", task: tasksFixtures.get(`TBD`) }, 23 | { 24 | type: "task_run", 25 | taskRun: taskRunsFixtures.get("TBD_1"), 26 | }, 27 | ), 28 | }, 29 | { 30 | no: 2, 31 | step: `TBD`, 32 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 33 | resource: createResourceFixtures( 34 | { type: "llm" }, 35 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 36 | { type: "task", task: tasksFixtures.get(`TBD`) }, 37 | { 38 | type: "task_run", 39 | taskRun: taskRunsFixtures.get("TBD_1"), 40 | }, 41 | ), 42 | }, 43 | ] as const satisfies TaskStepWithVariousResource[]; 44 | 45 | const fixtures = createFixtures(ENTRIES, ({ step }) => step); 46 | export default fixtures; 47 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/task-step.ts: -------------------------------------------------------------------------------- 1 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 2 | import { 3 | createResourceFixtures, 4 | TaskStepWithVariousResource, 5 | } from "../../../base/resource-fixtures.js"; 6 | import agentsFixtures from "./agent-config.js"; 7 | import toolsFixtures from "./tools.js"; 8 | import tasksFixtures from "./task-config.js"; 9 | import taskRunsFixtures from "./task-run.js"; 10 | import { TaskStepMapper } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/task-step-mapper.js"; 11 | 12 | type ToolName = FixtureName; 13 | 14 | const ENTRIES = [ 15 | { 16 | no: 1, 17 | step: `TBD`, 18 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 19 | resource: createResourceFixtures( 20 | { type: "tools", tools: ["TBD"] as ToolName[] }, 21 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 22 | { type: "task", task: tasksFixtures.get(`TBD`) }, 23 | { 24 | type: "task_run", 25 | taskRun: taskRunsFixtures.get("TBD_1"), 26 | }, 27 | ), 28 | }, 29 | { 30 | no: 2, 31 | step: `TBD`, 32 | ...TaskStepMapper.parseInputOutput(`input: TBD; output: TBD`), 33 | resource: createResourceFixtures( 34 | { type: "llm" }, 35 | { type: "agent", agent: agentsFixtures.get(`TBD`) }, 36 | { type: "task", task: tasksFixtures.get(`TBD`) }, 37 | { 38 | type: "task_run", 39 | taskRun: taskRunsFixtures.get("TBD_1"), 40 | }, 41 | ), 42 | }, 43 | ] as const satisfies TaskStepWithVariousResource[]; 44 | 45 | const fixtures = createFixtures(ENTRIES, ({ step }) => step); 46 | export default fixtures; 47 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/who-is/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChoiceExplanations, 3 | WorkflowComposeFixture, 4 | } from "../../../base/workflow-compose-fixtures.js"; 5 | import agentsFixtures from "./agent-config.js"; 6 | import tasksFixtures from "./task-config.js"; 7 | import taskStepsFixtures from "./task-step.js"; 8 | import taskRunsFixtures from "./task-run.js"; 9 | import toolsFixtures from "./tools.js"; 10 | 11 | const title = `TBD`; 12 | 13 | const prompt = `TBD`; 14 | 15 | const choiceExplanations = { 16 | requestHandler: `TBD`, 17 | problemDecomposer: `TBD`, 18 | steps: [ 19 | { 20 | no: 1, 21 | agentConfig: `TBD`, 22 | taskConfig: `TBD`, 23 | taskRun: `TBD`, 24 | }, 25 | { 26 | no: 2, 27 | agentConfig: `TBD`, 28 | taskConfig: `TBD`, 29 | taskRun: `TBD`, 30 | }, 31 | { 32 | no: 3, 33 | agentConfig: `TBD`, 34 | taskConfig: `TBD`, 35 | taskRun: `TBD`, 36 | }, 37 | { 38 | no: 4, 39 | agentConfig: `TBD`, 40 | taskConfig: `TBD`, 41 | taskRun: `TBD`, 42 | }, 43 | { 44 | no: 5, 45 | agentConfig: `TBD`, 46 | taskConfig: `TBD`, 47 | taskRun: `TBD`, 48 | }, 49 | { 50 | no: 6, 51 | agentConfig: `TBD`, 52 | taskConfig: `TBD`, 53 | taskRun: `TBD`, 54 | }, 55 | ], 56 | } satisfies ChoiceExplanations; 57 | 58 | export const requestHandlerOutput = `TBD`; 59 | 60 | const fixtures = new WorkflowComposeFixture( 61 | title, 62 | prompt, 63 | choiceExplanations, 64 | requestHandlerOutput, 65 | taskStepsFixtures, 66 | toolsFixtures, 67 | agentsFixtures, 68 | tasksFixtures, 69 | taskRunsFixtures, 70 | ); 71 | 72 | export default fixtures; 73 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/__template__/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChoiceExplanations, 3 | WorkflowComposeFixture, 4 | } from "../../../base/workflow-compose-fixtures.js"; 5 | import agentsFixtures from "./agent-config.js"; 6 | import tasksFixtures from "./task-config.js"; 7 | import taskStepsFixtures from "./task-step.js"; 8 | import taskRunsFixtures from "./task-run.js"; 9 | import toolsFixtures from "./tools.js"; 10 | 11 | const title = `TBD`; 12 | 13 | const prompt = `TBD`; 14 | 15 | const choiceExplanations = { 16 | requestHandler: `TBD`, 17 | problemDecomposer: `TBD`, 18 | steps: [ 19 | { 20 | no: 1, 21 | agentConfig: `TBD`, 22 | taskConfig: `TBD`, 23 | taskRun: `TBD`, 24 | }, 25 | { 26 | no: 2, 27 | agentConfig: `TBD`, 28 | taskConfig: `TBD`, 29 | taskRun: `TBD`, 30 | }, 31 | { 32 | no: 3, 33 | agentConfig: `TBD`, 34 | taskConfig: `TBD`, 35 | taskRun: `TBD`, 36 | }, 37 | { 38 | no: 4, 39 | agentConfig: `TBD`, 40 | taskConfig: `TBD`, 41 | taskRun: `TBD`, 42 | }, 43 | { 44 | no: 5, 45 | agentConfig: `TBD`, 46 | taskConfig: `TBD`, 47 | taskRun: `TBD`, 48 | }, 49 | { 50 | no: 6, 51 | agentConfig: `TBD`, 52 | taskConfig: `TBD`, 53 | taskRun: `TBD`, 54 | }, 55 | ], 56 | } satisfies ChoiceExplanations; 57 | 58 | export const requestHandlerOutput = `TBD`; 59 | 60 | const fixtures = new WorkflowComposeFixture( 61 | title, 62 | prompt, 63 | choiceExplanations, 64 | requestHandlerOutput, 65 | taskStepsFixtures, 66 | toolsFixtures, 67 | agentsFixtures, 68 | tasksFixtures, 69 | taskRunsFixtures, 70 | ); 71 | 72 | export default fixtures; 73 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 © BeeAI a Series of LF Projects, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import "dotenv/config"; 18 | import { defineConfig } from "vitest/config"; 19 | import tsConfigPaths from "vite-tsconfig-paths"; 20 | import path from "path"; 21 | 22 | export default defineConfig({ 23 | test: { 24 | include: [ 25 | "src/**/*.test.ts", 26 | "src/**/*.spec.ts", 27 | "tests/**/*.test.ts", 28 | "tests/**/*.spec.ts", 29 | ], 30 | environment: "node", 31 | globals: true, 32 | // setupFiles: ['tests/setup.ts'], 33 | passWithNoTests: true, 34 | testTimeout: 120 * 1000, 35 | printConsoleTrace: true, 36 | }, 37 | plugins: [ 38 | tsConfigPaths({ 39 | projects: ["tsconfig.json"], 40 | }), 41 | ], 42 | resolve: { 43 | alias: { 44 | "@": path.resolve(__dirname, "./src"), 45 | "@agents": path.resolve(__dirname, "./src/agents"), 46 | "@tasks": path.resolve(__dirname, "./src/tasks"), 47 | "@workspaces": path.resolve(__dirname, "./src/workspaces"), 48 | "@ui": path.resolve(__dirname, "./src/ui"), 49 | "@runtime": path.resolve(__dirname, "./src/runtime"), 50 | }, 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup mise environment' 2 | description: 'Install mise and set up tools from mise.toml' 3 | inputs: 4 | node-version: 5 | description: 'Node.js version to use (overrides .mise.toml)' 6 | required: false 7 | default: '' 8 | cache-enabled: 9 | description: 'Whether to enable caching' 10 | required: false 11 | default: 'true' 12 | cache-key-prefix: 13 | description: 'Prefix for the cache key' 14 | required: false 15 | default: 'mise' 16 | working-directory: 17 | description: 'Directory where .mise.toml is located' 18 | required: false 19 | default: '.' 20 | 21 | runs: 22 | using: "composite" 23 | steps: 24 | - name: Install mise 25 | uses: jdx/mise-action@5083fe46898c414b2475087cc79da59e7da859e8 26 | with: 27 | version: 2025.2.6 28 | 29 | - name: Set Node.js version 30 | if: inputs.node-version != '' 31 | shell: bash 32 | working-directory: ${{ inputs.working-directory }} 33 | run: | 34 | echo "node = \"${{ inputs.node-version }}\"" >> .mise.toml 35 | 36 | - name: Cache mise tools 37 | if: inputs.cache-enabled == 'true' 38 | uses: actions/cache@v3 39 | with: 40 | path: ~/.local/share/mise 41 | key: ${{ runner.os }}-${{ inputs.cache-key-prefix }}-${{ hashFiles(format('{0}/.mise.toml', inputs.working-directory)) }} 42 | restore-keys: | 43 | ${{ runner.os }}-${{ inputs.cache-key-prefix }}- 44 | 45 | - name: Install mise tools 46 | shell: bash 47 | working-directory: ${{ inputs.working-directory }} 48 | run: mise install 49 | 50 | - name: List installed tools 51 | shell: bash 52 | working-directory: ${{ inputs.working-directory }} 53 | run: mise list -------------------------------------------------------------------------------- /src/laml/README.md: -------------------------------------------------------------------------------- 1 | # LAML – Lightweight **Approximate** Markup Language 2 | 3 | > **LAML** is a YAML‑inspired, indentation‑based micro‑language designed to make 4 | > structured outputs from Large Language Models (LLMs) _easy to prompt for and 5 | > easy to parse_, while being tolerant of the kinds of “almost right” formatting 6 | > mistakes that LLMs occasionally produce. 7 | 8 | --- 9 | 10 | ## ✨ Why LAML? 11 | 12 | 1. **LLM‑friendly** – LAML is concise enough to embed inside a system prompt 13 | without blowing out the token budget. 14 | 2. **Single‑source protocol** – Define your data contract once with the 15 | TypeScript `ProtocolBuilder`; the same definition is 16 | - rendered into a human‑readable prompt snippet _and_ 17 | - used by the runtime `Parser` to turn model output into safe, typed objects. 18 | 3. **Forgiving by design** – Missing quotes, extra whitespace, or minor 19 | punctuation slips are automatically corrected by the parser so you get usable 20 | data instead of an error. 21 | 22 | ## 🚀 Quick start 23 | 24 | ```ts 25 | import { ProtocolBuilder, Parser } from "laml"; 26 | 27 | // 1️⃣ Define the output contract once 28 | const protocol = ProtocolBuilder.new() 29 | .text({ 30 | name: "title", 31 | description: "Blog post title", 32 | }) 33 | .array({ 34 | name: "tags", 35 | type: "text", 36 | description: "List of tags", 37 | }) 38 | .build(); 39 | 40 | // 2️⃣ Use it in your prompt 41 | const prompt = `Please answer in this format:\n\n${protocol.toString()}`; 42 | 43 | // …send the prompt to your favourite model… 44 | 45 | // 3️⃣ Parse the model response 46 | const parser = new Parser({ protocol }); 47 | const result = parser.parse(llmResponse); 48 | console.log(result); 49 | /* 50 | { 51 | title: "Hello, world!", 52 | tags: ["typescript", "laml"] 53 | } 54 | */ 55 | ``` 56 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/deep-sea-exploration/task-run.ts: -------------------------------------------------------------------------------- 1 | import { TaskRunMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-run-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskRunMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import taskConfigFixtures from "./task-config.js"; 5 | 6 | type TaskType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `sonar_mapping_underwater_terrain` satisfies TaskType, 10 | taskRunInput: `{ "zone_name": "Mariana Trench", "scan_resolution": "standard", "depth_range": "full" }`, 11 | taskRunNum: 1, 12 | }, 13 | { 14 | taskType: `sonar_mapping_underwater_terrain` satisfies TaskType, 15 | taskRunInput: `{ "zone_name": "Puerto Rico Trench", "scan_resolution": "standard", "depth_range": "full" }`, 16 | taskRunNum: 2, 17 | }, 18 | { 19 | taskType: `integrated_sonar_mapping` satisfies TaskType, 20 | taskRunInput: `{ "zone_name": "Mariana Trench", "scan_resolution": "standard", "depth_range": "full", "bio_frequency_range": "medium", "organism_filter": "all" }`, 21 | taskRunNum: 1, 22 | }, 23 | { 24 | taskType: `integrated_sonar_mapping` satisfies TaskType, 25 | taskRunInput: `{ "zone_name": "Puerto Rico Trench", "scan_resolution": "standard", "depth_range": "full", "bio_frequency_range": "medium", "organism_filter": "all" }`, 26 | taskRunNum: 2, 27 | }, 28 | { 29 | taskType: `generate_comprehensive_comparison_report` satisfies TaskType, 30 | taskRunInput: ``, 31 | taskRunNum: 1, 32 | }, 33 | ] as const satisfies TaskRunMinimal[]; 34 | 35 | export default createFixtures( 36 | addTaskRunMissingAttrs(ENTRIES), 37 | ({ taskType, taskRunNum }) => `${taskType}_${taskRunNum}`, 38 | ); 39 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/__tests__/helpers/mocks.ts: -------------------------------------------------------------------------------- 1 | import { addAgentConfigMissingAttrs } from "@/agents/supervisor/workflow/fixtures/helpers/add-missing-config-attrs.js"; 2 | import { JSONToolOutput, Logger } from "beeai-framework"; 3 | import { vi } from "vitest"; 4 | import { SUPERVISOR_AGENT_ID } from "../../../../../__test__/defaults.js"; 5 | import { AgentConfigInitializer } from "../../agent-config-initializer.js"; 6 | import { AgentConfigTiny } from "../../dto.js"; 7 | import { 8 | AgentConfigInitializerTool, 9 | AgentConfigInitializerToolResult, 10 | } from "../../tool.js"; 11 | 12 | export const getAgentConfigInitializerTool = ( 13 | logger: Logger, 14 | actingAgentId = SUPERVISOR_AGENT_ID, 15 | ) => { 16 | vi.spyOn( 17 | AgentConfigInitializerTool.prototype as unknown as { 18 | _run: AgentConfigInitializerTool["_run"]; 19 | }, 20 | "_run", 21 | ).mockImplementation(async (input) => { 22 | if (input.method === "createAgentConfig") { 23 | const { 24 | agentKind, 25 | config: { agentType, description, tools, instructions }, 26 | } = input; 27 | 28 | const data = addAgentConfigMissingAttrs( 29 | { 30 | agentType, 31 | description, 32 | tools, 33 | instructions, 34 | } satisfies AgentConfigTiny, 35 | { 36 | all: { 37 | agentKind, 38 | }, 39 | }, 40 | ); 41 | 42 | return new JSONToolOutput({ 43 | method: "createAgentConfig" as const, 44 | success: true, 45 | data, 46 | } satisfies AgentConfigInitializerToolResult); 47 | } 48 | 49 | throw new Error(`Unimplemented method mock: ${input.method}`); 50 | }); 51 | 52 | return new AgentConfigInitializer(logger, actingAgentId); 53 | }; 54 | -------------------------------------------------------------------------------- /src/tools/openf1/get-driver.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { z } from "zod"; 4 | import { 5 | FormattedToolOutput, 6 | formatToolDescription, 7 | formatToolOutput, 8 | } from "../helpers/tool-format.js"; 9 | import * as dto from "./dto.js"; 10 | import { OpenF1Service } from "./service.js"; 11 | 12 | export type OutputObject = dto.DriverDetail; 13 | export type GetDriverToolOutput = FormattedToolOutput; 14 | 15 | const example = { 16 | driver_id: 2, 17 | broadcast_name: "Lewis Hamilton", 18 | full_name: "Lewis William Hamilton", 19 | name_acronym: "HAM", 20 | team_name: "Mercedes-AMG Petronas Formula One Team", 21 | team_colour: "#00D2BE", 22 | first_name: "Lewis", 23 | last_name: "Hamilton", 24 | headshot_url: "https://example.com/headshot.jpg", 25 | country_code: "GB", 26 | } satisfies OutputObject; 27 | 28 | export class GetDriverTool extends Tool { 29 | name = "f1_get_driver"; 30 | description = formatToolDescription({ 31 | description: "Provides detailed information about a specific F1 driver.", 32 | example, 33 | }); 34 | 35 | static { 36 | this.register(); 37 | } 38 | 39 | public readonly emitter: ToolEmitter, GetDriverToolOutput> = 40 | Emitter.root.child({ 41 | namespace: ["tool", "f1_get_driver"], 42 | creator: this, 43 | }); 44 | 45 | inputSchema() { 46 | return z.object({ 47 | driverId: z.number().int(), 48 | sessionId: z.number().int(), 49 | }); 50 | } 51 | 52 | protected async _run(input: ToolInput) { 53 | const output = await OpenF1Service.getInstance().getDriver( 54 | input.driverId, 55 | input.sessionId, 56 | ); 57 | return formatToolOutput(output); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/ui/controls/navigation.ts: -------------------------------------------------------------------------------- 1 | export enum NavigationDirection { 2 | NEXT = "next", 3 | PREVIOUS = "previous", 4 | UP = "up", 5 | DOWN = "down", 6 | LEFT = "left", 7 | RIGHT = "right", 8 | IN = "in", 9 | OUT = "out", 10 | } 11 | 12 | export type Effect = () => void; 13 | 14 | export interface NavigationTransitions { 15 | next?: string; 16 | nextEffect?: Effect; 17 | previous?: string; 18 | previousEffect?: Effect; 19 | up?: string; 20 | upEffect?: Effect; 21 | down?: string; 22 | downEffect?: Effect; 23 | left?: string; 24 | leftEffect?: Effect; 25 | right?: string; 26 | rightEffect?: Effect; 27 | in?: string; 28 | inEffect?: Effect; 29 | out?: string; 30 | outEffect?: Effect; 31 | } 32 | 33 | export const NavigationDescription = { 34 | EXIT_APP: "Exit app", 35 | CANCEL: "Cancel", 36 | HIDE: "Hide", 37 | IN_OUT: "Navigate in/out", 38 | OUT: "Navigate out", 39 | LEFT_RIGHT: "Navigate left/right", 40 | TOGGLE_AUTO_POPUP: "Toggle auto popup", 41 | UP_DOWN: "Navigate up/down", 42 | NEXT_PREV: "Navigate next/prev", 43 | SELECT_CONTAINER: "Select container", 44 | SELECT_COLUMN: "Select left/right column", 45 | SELECT_INPUT: "Select input", 46 | ESCAPE_INPUT_MODE: "Escape input mode", 47 | ENTER_INPUT_MODE: "Enter input mode", 48 | MOVE_LEFT_RIGHT: "Move left/right", 49 | MOVE_UP_DOWN: "Move up/down", 50 | MOVE_UP_DOWN_PAGE: "Move up/down one page", 51 | MOVE_START_END: "Move to start/end", 52 | MOVE_PREV_NEXT_WORD: "Move to prev/next word", 53 | MOVE_START_END_LINE: "Move to line start/end", 54 | HIGHLIGHT_NEXT_PREV: "Highlight next/prev message", 55 | COPY_MESSAGE: "Copy highlighted message", 56 | MESSAGES_FILTER: "Messages filter", 57 | ROLE_FILTER: "Roles filter", 58 | MESSAGES: "Messages", 59 | CHAT: "Chat", 60 | WORKFLOW_EXPLORER: "Workflow explorer", 61 | SEND_MESSAGE: "Send message", 62 | } as const; 63 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/beekeeping-site-analysis/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | // Location Analysis Tools (each essential but incomplete) 6 | { 7 | toolName: "satellite_flora_scanner_api", 8 | description: 9 | "Scans location using satellite imagery to identify visible plant species. Provides species names but cannot determine nectar quality, bloom timing, or pollinator relationships.", 10 | toolInput: '{"location":""}', 11 | }, 12 | { 13 | toolName: "ground_survey_validator_api", 14 | description: 15 | "Validates and enriches satellite data with ground-level plant health and density information. Requires species list from satellite scanning to focus the ground survey.", 16 | toolInput: '{"location":"","target_species":[""]}', 17 | }, 18 | { 19 | toolName: "pollinator_database_lookup_api", 20 | description: 21 | "Looks up nectar production rates and butterfly host plant compatibility for identified species. Requires validated species list as input.", 22 | toolInput: 23 | '{"plant_species":[""],"lookup_type":"nectar_production|host_compatibility|both"}', 24 | }, 25 | // Reporting Tools (require multiple inputs) 26 | { 27 | toolName: "comparative_report_generator_api", 28 | description: 29 | "Generates structured comparison reports between locations. Requires suitability calculations from all analyzed locations.", 30 | toolInput: 31 | '{"location_suitability_scores":[""],"report_format":"detailed","include_recommendations":true}', 32 | }, 33 | ] as const satisfies AgentAvailableTool[]; 34 | 35 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 36 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/poetry-song-analysis/agent-config.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigTiny } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/dto.js"; 2 | import { createFixtures } from "../../base/fixtures.js"; 3 | import { addAgentConfigMissingAttrs } from "../../helpers/add-missing-config-attrs.js"; 4 | // import toolsFixtures from "./tools.js"; 5 | 6 | // type ToolName = FixtureName; 7 | 8 | const ENTRIES = [ 9 | // { 10 | // agentType: "booking_options_compiler", 11 | // description: 12 | // "Compiles a list of booking options for flights and hotels that match user criteria, including total price and direct booking links, using LLM capabilities.", 13 | // instructions: `Context: You are an agent specializing in compiling booking options for flights and hotels. You are activated by an external task and receive options within budget as input. You rely on LLM capabilities to generate a final list of booking options. 14 | // Objective: Use the provided options within budget to compile a list of booking options for flights and hotels. Include total price and direct booking links in the results. 15 | // Response format: List each booking option with its flight details, hotel details, total price, and direct booking links: 16 | // Final Booking Options: 17 | // 1. Flight: [Flight Details 1] — Hotel: [Hotel Details 1] — Total Price: [Total Price 1] — Booking Links: [Flight Booking Link 1, Hotel Booking Link 1] 18 | // 2. Flight: [Flight Details 2] — Hotel: [Hotel Details 2] — Total Price: [Total Price 2] — Booking Links: [Flight Booking Link 2, Hotel Booking Link 2]`, 19 | // tools: [] as const satisfies ToolName[], 20 | // }, 21 | ] as const satisfies AgentConfigTiny[]; 22 | 23 | export default createFixtures( 24 | addAgentConfigMissingAttrs(ENTRIES), 25 | ({ agentType }) => agentType, 26 | ); 27 | -------------------------------------------------------------------------------- /src/agents/operator.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import { ArXivTool } from "beeai-framework/tools/arxiv"; 3 | import { DuckDuckGoSearchTool } from "beeai-framework/tools/search/duckDuckGoSearch"; 4 | import { BaseToolsFactory, ToolFactoryMethod } from "@/base/tools-factory.js"; 5 | import { getEnv } from "beeai-framework/internals/env"; 6 | import { TavilySearchTool } from "@/tools/tavily/tavily-search.js"; 7 | import { TavilyExtractTool } from "@/tools/tavily/tavily-extract.js"; 8 | import { ListSeasonsTool } from "@/tools/openf1/list-seasons.js"; 9 | import { GetSeasonDetailTool } from "@/tools/openf1/get-season-detail.js"; 10 | import { GetDriverTool } from "@/tools/openf1/get-driver.js"; 11 | import { GetCurrentSeasonDetailTool } from "@/tools/openf1/get-current-season-detail.js"; 12 | import { ListGrandPrixPositionsTool } from "@/tools/openf1/list-grandprix-positions.js"; 13 | import { GetGrandPrixDetailTool } from "@/tools/openf1/get-grand-prix-detail.js"; 14 | 15 | export class ToolsFactory extends BaseToolsFactory { 16 | async getFactoriesMethods(): Promise { 17 | return [ 18 | // ...getWebSearchTools(), 19 | // () => new ArXivTool(), 20 | () => new ListSeasonsTool(), 21 | () => new ListGrandPrixPositionsTool(), 22 | () => new GetCurrentSeasonDetailTool(), 23 | () => new GetSeasonDetailTool(), 24 | () => new GetDriverTool(), 25 | () => new GetGrandPrixDetailTool(), 26 | ]; 27 | } 28 | } 29 | type WebSearchToolName = "tavily" | "duckduckgo"; 30 | 31 | function getWebSearchTools() { 32 | const searchTool: WebSearchToolName = 33 | getEnv("SEARCH_TOOL") === "tavily" ? "tavily" : "duckduckgo"; 34 | switch (searchTool) { 35 | case "tavily": 36 | return [() => new TavilySearchTool(), () => new TavilyExtractTool()]; 37 | case "duckduckgo": 38 | return [() => new DuckDuckGoSearchTool()]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/tools/openf1/get-current-season-detail.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { 4 | FormattedToolOutput, 5 | formatToolDescription, 6 | formatToolOutput, 7 | } from "../helpers/tool-format.js"; 8 | import * as dto from "./dto.js"; 9 | import { OpenF1Service } from "./service.js"; 10 | 11 | export type OutputObject = dto.SeasonDetail; 12 | export type GetCurrentSeasonDetailToolOutput = 13 | FormattedToolOutput; 14 | 15 | const example = { 16 | season_id: 0, 17 | year: 2023, 18 | state: "completed", 19 | grands_prix: [ 20 | { 21 | grand_prix_id: 1219, 22 | name: "Singapore Grand Prix", 23 | date: new Date("2023-09-15T09:30:00Z"), 24 | }, 25 | { 26 | grand_prix_id: 1141, 27 | name: "Bahrain Grand Prix", 28 | date: new Date("2023-03-05T13:00:00Z"), 29 | }, 30 | ], 31 | } satisfies OutputObject; 32 | 33 | export class GetCurrentSeasonDetailTool extends Tool { 34 | name = "f1_get_current_season_detail"; 35 | description = formatToolDescription({ 36 | description: 37 | "Provides detailed information about current F1 season, including the list of Grands Prix, their dates, and locations.", 38 | example, 39 | }); 40 | 41 | static { 42 | this.register(); 43 | } 44 | 45 | public readonly emitter: ToolEmitter< 46 | ToolInput, 47 | GetCurrentSeasonDetailToolOutput 48 | > = Emitter.root.child({ 49 | namespace: ["tool", "f1_get_current_season_detail"], 50 | creator: this, 51 | }); 52 | 53 | inputSchema() { 54 | return {}; 55 | } 56 | 57 | protected async _run() { 58 | const output = await OpenF1Service.getInstance().getSeasonDetail( 59 | new Date().getFullYear(), 60 | ); 61 | 62 | return formatToolOutput(output); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/tools/openf1/list-grandprix-positions.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { z } from "zod"; 4 | import { 5 | FormattedToolOutput, 6 | formatToolDescription, 7 | formatToolOutput, 8 | } from "../helpers/tool-format.js"; 9 | import * as dto from "./dto.js"; 10 | import { OpenF1Service } from "./service.js"; 11 | 12 | export type OutputObject = dto.Position[]; 13 | export type ListGrandPrixPositionsToolOutput = 14 | FormattedToolOutput; 15 | 16 | const example = [ 17 | { 18 | position: 1, 19 | driver: { 20 | driver_id: 123, 21 | full_name: "Lewis William", 22 | }, 23 | }, 24 | { 25 | position: 2, 26 | driver: { 27 | driver_id: 45, 28 | full_name: "Charles Borne", 29 | }, 30 | }, 31 | ] satisfies OutputObject; 32 | 33 | export class ListGrandPrixPositionsTool extends Tool { 34 | name = "f1_list_grand_prix_positions"; 35 | description = formatToolDescription({ 36 | description: 37 | "Provides list of positions for a specific Grand Prix for qualification or race.", 38 | example, 39 | }); 40 | 41 | static { 42 | this.register(); 43 | } 44 | 45 | public readonly emitter: ToolEmitter< 46 | ToolInput, 47 | ListGrandPrixPositionsToolOutput 48 | > = Emitter.root.child({ 49 | namespace: ["tool", "f1_list_grand_prix_positions"], 50 | creator: this, 51 | }); 52 | 53 | inputSchema() { 54 | return z.object({ 55 | grand_prix_id: z.number().int(), 56 | type: dto.SessionPositionTypeEnumSchema, 57 | }); 58 | } 59 | 60 | protected async _run(input: ToolInput) { 61 | const output = await OpenF1Service.getInstance().listPositions( 62 | input.grand_prix_id, 63 | input.type, 64 | "finish", 65 | ); 66 | return formatToolOutput(output); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/workflow-explorer/components/phases.ts: -------------------------------------------------------------------------------- 1 | import blessed from "neo-blessed"; 2 | 3 | // Define update types as const to ensure type safety 4 | export enum PhaseType { 5 | WORKFLOW_INPUT = "workflow_input", 6 | REQUEST_HANDLER = "request_handler", 7 | PROBLEM_DECOMPOSER = "problem_decomposer", 8 | TASK_STEP = "task_step", 9 | WORKFLOW_OUTPUT = "workflow_output", 10 | } 11 | 12 | const PhasesOrder = [ 13 | PhaseType.WORKFLOW_INPUT, 14 | PhaseType.REQUEST_HANDLER, 15 | PhaseType.PROBLEM_DECOMPOSER, 16 | PhaseType.TASK_STEP, 17 | PhaseType.WORKFLOW_OUTPUT, 18 | ]; 19 | 20 | export interface PhasesItem { 21 | type: PhaseType; 22 | index?: number; 23 | label: string; 24 | } 25 | 26 | export interface PhasesInput { 27 | items: PhasesItem[]; 28 | selectedType: PhaseType; 29 | } 30 | 31 | export class Phases { 32 | private _element: blessed.Widgets.TextElement; 33 | 34 | get element() { 35 | return this._element; 36 | } 37 | 38 | constructor(parent: blessed.Widgets.Node) { 39 | this._element = blessed.text({ 40 | parent, 41 | top: 0, 42 | focusable: false, 43 | keys: false, 44 | mouse: false, 45 | tags: true, 46 | hidden: true, // Initially hidden 47 | style: { 48 | // bold: true, 49 | fg: "white", 50 | }, 51 | }); 52 | } 53 | 54 | render(input: PhasesInput) { 55 | const selectedIndex = PhasesOrder.indexOf(input.selectedType); 56 | if (selectedIndex === -1) { 57 | throw new Error(`Invalid selectedType: ${input.selectedType}`); 58 | } 59 | const items = input.items.map((item, index) => { 60 | const isSelected = index === selectedIndex; 61 | const label = isSelected 62 | ? `{white-fg}{green-bg} ${item.label} {/green-bg}{/white-fg}` 63 | : item.label; 64 | return label; 65 | }); 66 | 67 | this._element.setContent(`${items.join(" ➜ ")}`); 68 | this._element.screen.render(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/__tests__/helpers/mocks.ts: -------------------------------------------------------------------------------- 1 | import { addTaskConfigMissingAttrs } from "@/agents/supervisor/workflow/fixtures/helpers/add-missing-config-attrs.js"; 2 | import { JSONToolOutput, Logger } from "beeai-framework"; 3 | import { vi } from "vitest"; 4 | import { SUPERVISOR_AGENT_ID } from "../../../../../__test__/defaults.js"; 5 | import { TaskConfigMinimal } from "../../dto.js"; 6 | import { TaskConfigInitializer } from "../../task-config-initializer.js"; 7 | import { 8 | TaskConfigInitializerTool, 9 | TaskConfigInitializerToolResult, 10 | } from "../../tool.js"; 11 | 12 | export const getTaskConfigInitializerTool = ( 13 | logger: Logger, 14 | actingAgentId = SUPERVISOR_AGENT_ID, 15 | ) => { 16 | vi.spyOn( 17 | TaskConfigInitializerTool.prototype as unknown as { 18 | _run: TaskConfigInitializerTool["_run"]; 19 | }, 20 | "_run", 21 | ).mockImplementation(async (input) => { 22 | if (input.method === "createTaskConfig") { 23 | const { 24 | taskKind, 25 | taskType, 26 | agentKind, 27 | agentType, 28 | taskConfigInput, 29 | description, 30 | } = input.config; 31 | 32 | const data = addTaskConfigMissingAttrs( 33 | { 34 | taskType, 35 | taskConfigInput, 36 | description, 37 | agentType, 38 | } satisfies TaskConfigMinimal, 39 | { 40 | all: { 41 | taskKind, 42 | agentKind, 43 | }, 44 | }, 45 | ); 46 | 47 | // Mocking the response for the agent config creation 48 | return new JSONToolOutput({ 49 | method: "createTaskConfig" as const, 50 | success: true, 51 | data, 52 | } satisfies TaskConfigInitializerToolResult); 53 | } 54 | 55 | throw new Error(`Unexpected method: ${input.method}`); 56 | }); 57 | 58 | return new TaskConfigInitializer(logger, actingAgentId); 59 | }; 60 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/problem-decomposer/protocol.ts: -------------------------------------------------------------------------------- 1 | import * as laml from "@/laml/index.js"; 2 | 3 | export const protocol = laml.ProtocolBuilder.new() 4 | .text({ 5 | name: "RESPONSE_CHOICE_EXPLANATION", 6 | description: 7 | "Brief explanation of *why* you selected the given RESPONSE_TYPE", 8 | }) 9 | .constant({ 10 | name: "RESPONSE_TYPE", 11 | values: ["STEP_SEQUENCE", "UNSOLVABLE", "MISSING_INPUTS"] as const, 12 | description: "Valid values: STEP_SEQUENCE | UNSOLVABLE | MISSING_INPUTS", 13 | }) 14 | .comment({ 15 | comment: 16 | "Follow by one of the possible responses format based on the chosen response type", 17 | }) 18 | .object({ 19 | name: "RESPONSE_STEP_SEQUENCE", 20 | isOptional: true, 21 | attributes: laml.ProtocolBuilder.new().list({ 22 | name: "step_sequence", 23 | type: "numbered", 24 | description: `Ordered list of high-level tasks that collectively achieve the user's goal, each written as "Imperative task description (input: …, output: …) [resource]" where [resource] is exactly one of [task: name], [agent: name], [tools: tool1, …], or [LLM]. If user provides an explicit set of subtasks, map each step 1:1 to those subtasks, combining multiple tools inside a single step if needed.`, 25 | }), 26 | }) 27 | .object({ 28 | name: "RESPONSE_UNSOLVABLE", 29 | isOptional: true, 30 | attributes: laml.ProtocolBuilder.new().text({ 31 | name: "explanation", 32 | description: 33 | "User-facing explanation of why the request cannot be solved.", 34 | }), 35 | }) 36 | .object({ 37 | name: "RESPONSE_MISSING_INPUTS", 38 | isOptional: true, 39 | attributes: laml.ProtocolBuilder.new().text({ 40 | name: "explanation", 41 | description: 42 | "User-facing explanation of clearly identifying missing inputs and suggesting what the user could provide to make it solvable. Include examples where possible.", 43 | }), 44 | }) 45 | .build(); 46 | -------------------------------------------------------------------------------- /src/tools/openf1/get-season-detail.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { z } from "zod"; 4 | import { 5 | FormattedToolOutput, 6 | formatToolDescription, 7 | formatToolOutput, 8 | } from "../helpers/tool-format.js"; 9 | import { F1_AVAILABLE_FIRST_SEASON_YEAR } from "./client.js"; 10 | import { SeasonDetail } from "./dto.js"; 11 | import { OpenF1Service } from "./service.js"; 12 | 13 | export type OutputObject = SeasonDetail; 14 | export type GetSeasonDetailToolOutput = FormattedToolOutput; 15 | 16 | const example = { 17 | season_id: 123, 18 | year: 2023, 19 | state: "completed", 20 | grands_prix: [ 21 | { 22 | grand_prix_id: 1219, 23 | name: "Singapore Grand Prix", 24 | date: new Date("2023-09-15T09:30:00Z"), 25 | }, 26 | ], 27 | } satisfies OutputObject; 28 | 29 | export class GetSeasonDetailTool extends Tool { 30 | name = "f1_get_season_detail"; 31 | description = formatToolDescription({ 32 | description: `Provides detailed information about a specific F1 season, including the list of Grands Prix, their dates, and locations.`, 33 | example, 34 | }); 35 | 36 | static { 37 | this.register(); 38 | } 39 | 40 | public readonly emitter: ToolEmitter< 41 | ToolInput, 42 | GetSeasonDetailToolOutput 43 | > = Emitter.root.child({ 44 | namespace: ["tool", "f1_get_season_detail"], 45 | creator: this, 46 | }); 47 | 48 | inputSchema() { 49 | return z.object({ 50 | year: z 51 | .number() 52 | .int() 53 | .min(F1_AVAILABLE_FIRST_SEASON_YEAR) 54 | .max(new Date().getFullYear()), 55 | }); 56 | } 57 | 58 | protected async _run(input: ToolInput) { 59 | const output = await OpenF1Service.getInstance().getSeasonDetail( 60 | input.year, 61 | ); 62 | return formatToolOutput(output); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-run-initializer/__tests__/helpers/mocks.ts: -------------------------------------------------------------------------------- 1 | import { addTaskRunMissingAttrs } from "@/agents/supervisor/workflow/fixtures/helpers/add-missing-config-attrs.js"; 2 | import { JSONToolOutput, Logger } from "beeai-framework"; 3 | import { vi } from "vitest"; 4 | import { TaskRunMinimal } from "../../dto.js"; 5 | import { 6 | TaskRunInitializerTool, 7 | TaskRunInitializerToolResult, 8 | } from "../../tool.js"; 9 | import { SUPERVISOR_AGENT_ID } from "@/agents/supervisor/workflow/__test__/defaults.js"; 10 | import { TaskRunInitializer } from "../../task-run-initializer.js"; 11 | 12 | export const getTaskRunInitializerTool = ( 13 | logger: Logger, 14 | actingAgentId = SUPERVISOR_AGENT_ID, 15 | ) => { 16 | vi.spyOn( 17 | TaskRunInitializerTool.prototype as unknown as { 18 | _run: TaskRunInitializerTool["_run"]; 19 | }, 20 | "_run", 21 | ).mockImplementation(async (input) => { 22 | if (input.method === "createTaskRun") { 23 | const { 24 | taskRunInput, 25 | taskType, 26 | actingAgentId, 27 | originTaskRunId, 28 | blockedByTaskRunIds, 29 | } = input; 30 | 31 | const data = addTaskRunMissingAttrs( 32 | { 33 | taskType, 34 | taskRunInput, 35 | taskRunNum: 1, 36 | } satisfies TaskRunMinimal, 37 | { 38 | all: { 39 | taskKind: "operator", 40 | ownerAgentId: actingAgentId, 41 | originTaskRunId, 42 | blockedByTaskRunIds, 43 | }, 44 | }, 45 | ); 46 | 47 | // Mocking the response for the agent config creation 48 | return new JSONToolOutput({ 49 | method: "createTaskRun" as const, 50 | success: true, 51 | data, 52 | } satisfies TaskRunInitializerToolResult); 53 | } 54 | 55 | throw new Error(`Unexpected method: ${input.method}`); 56 | }); 57 | 58 | return new TaskRunInitializer(logger, actingAgentId); 59 | }; 60 | -------------------------------------------------------------------------------- /src/ui/controls/key-bindings.ts: -------------------------------------------------------------------------------- 1 | import blessed from "neo-blessed"; 2 | import { clone } from "remeda"; 3 | import { ControlsManager } from "./controls-manager.js"; 4 | 5 | export type KeyActionListener = ( 6 | ch: any, 7 | key: blessed.Widgets.Events.IKeyEventArg, 8 | ) => void; 9 | 10 | export type KeyActionListenerFactory = ( 11 | originEventId: string, 12 | ) => (ch: any, key: blessed.Widgets.Events.IKeyEventArg) => void; 13 | 14 | /** 15 | * Listener wrapper method to prevent firing listener in the same call as it was set up. 16 | * 17 | * @param listener 18 | * @returns 19 | */ 20 | export function keyActionListenerFactory( 21 | listener: KeyActionListener, 22 | ): KeyActionListenerFactory { 23 | return (originEventId: string) => 24 | (ch: any, key: blessed.Widgets.Events.IKeyEventArg) => { 25 | if (originEventId === ControlsManager.lastKeypressEventId) { 26 | return; 27 | } 28 | listener(ch, key); 29 | }; 30 | } 31 | 32 | export interface Action { 33 | listener: KeyActionListenerFactory; 34 | description: string; 35 | } 36 | 37 | export interface KeyAction { 38 | key: string | string[]; 39 | action: Action; 40 | } 41 | 42 | export interface KeyActions { 43 | kind: "exclusive" | "override"; 44 | actions: KeyAction[]; 45 | } 46 | 47 | export interface KeyBindings { 48 | keys: Map; 49 | } 50 | 51 | export function createKeyBindings(keyActions: KeyActions[]): KeyBindings { 52 | const bindings = { keys: new Map() } satisfies KeyBindings; 53 | const { keys } = bindings; 54 | for (const { kind, actions } of keyActions) { 55 | if (kind === "exclusive") { 56 | keys.clear(); 57 | } 58 | 59 | for (const a of actions) { 60 | const keysArray = Array.isArray(a.key) ? a.key.slice() : [a.key]; 61 | for (const k of keysArray) { 62 | if (kind === "override" || !keys.has(k)) { 63 | keys.set(k, clone(a.action)); 64 | } 65 | } 66 | } 67 | } 68 | 69 | return bindings; 70 | } 71 | -------------------------------------------------------------------------------- /src/laml/parser-output.ts: -------------------------------------------------------------------------------- 1 | import { clone } from "remeda"; 2 | import { LAMLObject, LAMLValue } from "./dto.js"; 3 | import { pathStr } from "./utils.js"; 4 | 5 | export class ParserOutput { 6 | protected _result: LAMLObject = {}; 7 | 8 | get result() { 9 | return clone(this._result) as TResult; 10 | } 11 | 12 | set(path: string[], value: LAMLValue) { 13 | let idx = 0; 14 | let temp = this._result; 15 | const currentPath = []; 16 | for (const pathPart of path) { 17 | const isLast = idx + 1 === path.length; 18 | currentPath.push(pathPart); 19 | if (!isLast) { 20 | let obj = temp[pathPart]; 21 | if (!obj) { 22 | obj = {}; 23 | temp[pathPart] = obj; 24 | temp = obj; 25 | } else if (typeof obj !== "object") { 26 | throw new Error( 27 | `Cannot access property on primitive value at path '${pathStr(currentPath)}'. Expected an object but received a primitive type.`, 28 | ); 29 | } else { 30 | temp = obj as LAMLObject; 31 | } 32 | } else { 33 | temp[pathPart] = value; 34 | } 35 | idx++; 36 | } 37 | } 38 | 39 | get(path: string[]) { 40 | let idx = 0; 41 | let temp = this._result; 42 | const currentPath = []; 43 | for (const pathPart of path) { 44 | currentPath.push(pathPart); 45 | const isLast = idx + 1 === path.length; 46 | if (!isLast) { 47 | const obj = temp[pathPart]; 48 | if (!obj) { 49 | throw new Error(`Missing object at path '${pathStr(currentPath)}'.`); 50 | } else if (typeof obj !== "object") { 51 | throw new Error( 52 | `Cannot access property on primitive value at path '${pathStr(currentPath)}'. Expected an object but received a primitive type.`, 53 | ); 54 | } else { 55 | temp = obj as LAMLObject; 56 | } 57 | } else { 58 | return temp[pathPart]; 59 | } 60 | idx++; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/narrative-fusion/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "generate_short_story", 11 | agentType: "short_story_generator" satisfies AgentType, 12 | description: 13 | "Generate a short story based on a provided . Ensure it has a clear beginning, middle, and end, creatively incorporating the concept.", 14 | taskConfigInput: `{"story_concept":""}`, 15 | }, 16 | { 17 | taskType: "merge_short_stories_into_screenplay_scene", 18 | agentType: "screenplay_scene_creator" satisfies AgentType, 19 | description: 20 | "Create a screenplay scene that creatively merges elements from multiple provided short stories. Ensure the scene is coherent and engaging, with well-structured dialogue and action descriptions that integrate the themes and characters from the input stories.", 21 | taskConfigInput: `{"short_stories":["", "..."]}`, 22 | }, 23 | { 24 | taskType: "analyze_screenplay_scene_convergence", 25 | agentType: "screenplay_scene_analyst" satisfies AgentType, 26 | description: 27 | "Provide a detailed analytical breakdown of how different narratives and themes converge within a provided . Highlight the integration of narratives, character interactions, and thematic elements.", 28 | taskConfigInput: `{"screenplay_scene":""}`, 29 | }, 30 | ] as const satisfies TaskConfigMinimal[]; 31 | 32 | export default createFixtures( 33 | addTaskConfigMissingAttrs(ENTRIES), 34 | ({ taskType }) => taskType, 35 | ); 36 | -------------------------------------------------------------------------------- /src/base/state/base-state-logger.ts: -------------------------------------------------------------------------------- 1 | import { 2 | appendFileSync, 3 | copyFileSync, 4 | existsSync, 5 | mkdirSync, 6 | truncateSync, 7 | writeFileSync, 8 | } from "fs"; 9 | import { dirname, join } from "path"; 10 | import { z } from "zod"; 11 | import { LogInit, LogUpdate } from "./dto.js"; 12 | 13 | export class BaseStateLogger { 14 | protected logPath: string; 15 | 16 | constructor( 17 | logFileDefaultPath: readonly string[], 18 | logFileDefaultName: string, 19 | logDirPath?: string, 20 | ) { 21 | this.logPath = join( 22 | logDirPath ?? process.cwd(), 23 | ...logFileDefaultPath, 24 | `${logFileDefaultName}.log`, 25 | ); 26 | 27 | this.rotateLogFileIfExists(); 28 | this.logInit(); 29 | } 30 | 31 | private rotateLogFileIfExists(): void { 32 | // Ensure the directory exists 33 | const dirPath = dirname(this.logPath); 34 | if (!existsSync(dirPath)) { 35 | mkdirSync(dirPath, { recursive: true }); 36 | } 37 | 38 | if (existsSync(this.logPath)) { 39 | // Generate timestamp for the backup file 40 | const timestamp = new Date() 41 | .toISOString() 42 | .replace(/:/g, "-") // Replace colons with dashes for file name compatibility 43 | .replace(/\./g, "-"); // Replace dots with dashes 44 | 45 | // Create backup file path 46 | const backupPath = this.logPath.replace(".log", `.${timestamp}.log`); 47 | 48 | // Copy existing file to backup 49 | copyFileSync(this.logPath, backupPath); 50 | 51 | // Clear the contents of the original file 52 | truncateSync(this.logPath, 0); 53 | } else { 54 | // Create new empty log file if it doesn't exist 55 | writeFileSync(this.logPath, ""); 56 | } 57 | } 58 | 59 | private logInit() { 60 | this.logUpdate({ type: "@log_init" }); 61 | } 62 | 63 | protected logUpdate( 64 | update: Omit, "timestamp"> | Omit, 65 | ) { 66 | const timestamp = new Date().toISOString(); 67 | appendFileSync( 68 | this.logPath, 69 | JSON.stringify({ ...update, timestamp }) + "\n", 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/beekeeping-site-analysis/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: "analyze_flora_for_nectar_sources", 10 | agentType: "flora_nectar_analysis" satisfies AgentType, 11 | description: 12 | "Analyze local flora at a specified to determine nectar sources suitable for beekeeping. Utilize satellite imagery and ground survey data to identify plant species, validate their presence and health, and assess their nectar production suitability.", 13 | taskConfigInput: `{"location": ""}`, 14 | }, 15 | { 16 | taskType: "analyze_flora_for_butterfly_host_plants", 17 | agentType: "flora_butterfly_host_analysis" satisfies AgentType, 18 | description: 19 | "Analyze local flora at a specified to determine nectar sources suitable for butterfly host plants. Utilize satellite imagery and ground survey data to identify plant species, validate their presence and health, and assess their compatibility as host plants for butterflies.", 20 | taskConfigInput: `{"location": ""}`, 21 | }, 22 | { 23 | taskType: `compile_farming_suitability_report`, 24 | agentType: `report_compiler_for_farming_suitability` satisfies AgentType, 25 | description: `Compile findings from nectar suitability and butterfly host compatibility analyses into a structured report. Highlight key findings and provide recommendations for beekeeping and butterfly farming at each analyzed site.`, 26 | taskConfigInput: `{"nectar_suitability_data": ["", "..."], "butterfly_host_compatibility_data": ["", "..."]}`, 27 | }, 28 | ] as const satisfies TaskConfigMinimal[]; 29 | 30 | export default createFixtures( 31 | addTaskConfigMissingAttrs(ENTRIES), 32 | ({ taskType }) => taskType, 33 | ); 34 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/smart-farm-harvest-planner/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: "field_info_api", 7 | description: 8 | "Returns agronomic metadata and coordinates for a given field, such as crop type, area, and moisture constraints.", 9 | toolInput: '{"fieldId":""}', 10 | }, 11 | { 12 | toolName: "equipment_registry_api", 13 | description: 14 | "Lists all equipment units (harvesters, drones, dryers, etc.) assigned to a specific field or farm zone.", 15 | toolInput: '{"fieldId":""}', 16 | }, 17 | { 18 | toolName: "weather_forecast_api", 19 | description: 20 | "Returns hourly precipitation probability and wind speed for a geofence.", 21 | toolInput: 22 | '{"lat":,"lon":,"startISO":"","hours":}', 23 | }, 24 | { 25 | toolName: "equipment_status_api", 26 | description: 27 | "Checks operational readiness of harvesters, drones, and grain dryers.", 28 | toolInput: '{"equipmentIds":[""],"detailLevel":"basic|full"}', 29 | }, 30 | { 31 | toolName: "harvest_scheduler_api", 32 | description: 33 | "Creates an optimised field-order and time-block plan given machinery, forecast, and moisture limits.", 34 | toolInput: 35 | '{"fields":[{"id":"","areaHa":}],"availableEquipment":[{"equipmentId":""}],"weather":[/* output from weather_forecast_api */],"maxMoisturePercent":}', 36 | }, 37 | { 38 | toolName: "timeline_report_generator", 39 | description: 40 | "Generates a human-readable timeline with equipment assignments and contingency notes, based on the harvest schedule and weather outlook.", 41 | toolInput: 42 | '{"harvestSchedule":{/* output from harvest_scheduler_api */},"weatherForecast":{/* output from weather_forecast_api */}}', 43 | }, 44 | ] as const satisfies AgentAvailableTool[]; 45 | 46 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 47 | -------------------------------------------------------------------------------- /src/tools/tavily/tavily-extract.ts: -------------------------------------------------------------------------------- 1 | import { JSONToolOutput, Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { z } from "zod"; 4 | import { getClient } from "./client.js"; 5 | 6 | export type TavilyExtractToolOutput = { 7 | url: string; 8 | rawContent: string; 9 | }[]; 10 | 11 | export class TavilyExtractTool extends Tool< 12 | JSONToolOutput 13 | > { 14 | name = "tavily_page_extract"; 15 | description = 16 | "A focused content-retrieval endpoint that fetches the full readable text (and, where available, metadata such as title, author, publish date, alt-text, and canonical URL) from one or more specific webpages you already know the addresses of; invoke it after a search—or whenever the user supplies or requests exact URLs—when you must quote, summarize, fact-check, extract tables/code/snippets, or reason over details that are not reliably captured in snippets alone, while skipping it if (a) the question can be answered from your own knowledge or search snippets, (b) the site is pay-walled, requires login, or hosts dynamic content that scraping would miss, or (c) the user forbids browsing; call it with a JSON object whose urls field is a list of absolute URLs (add optional max_chars, include_images, or selector keys if supported) and then parse the returned plain text or structured data, keeping network calls minimal by batching related URLs, respecting copyright, and citing any extracted material."; 17 | 18 | static { 19 | this.register(); 20 | } 21 | 22 | public readonly emitter: ToolEmitter< 23 | ToolInput, 24 | JSONToolOutput 25 | > = Emitter.root.child({ 26 | namespace: ["tool", "tavily_page_extract"], 27 | creator: this, 28 | }); 29 | 30 | inputSchema() { 31 | return z.object({ 32 | urls: z.array(z.string({ description: `Page url` })), 33 | }); 34 | } 35 | 36 | protected async _run(input: ToolInput) { 37 | const client = getClient(); 38 | const { results } = await client.extract(input.urls, { 39 | extractDepth: "basic", 40 | includeImages: false, 41 | }); 42 | return new JSONToolOutput(results); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/asteroid-mining-feasibility/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `analyze_asteroid_mineral_composition`, 10 | agentType: `asteroid_mineral_composition_analyzer` satisfies AgentType, 11 | description: `Analyze the mineral composition of a specified asteroid using its ID and the desired analysis depth. Utilize spectroscopic data to provide raw element percentages and mineral identification.`, 12 | taskConfigInput: `{"asteroid_id": "", "analysis_depth": ""}`, 13 | }, 14 | { 15 | taskType: `cross_reference_mineral_composition_with_orbital_mechanics`, 16 | agentType: `asteroid_mission_planner` satisfies AgentType, 17 | description: `Cross-reference the provided mineral composition data of a specified asteroid with its orbital mechanics calculations to assist in comprehensive mission planning.`, 18 | taskConfigInput: `{"asteroid_id": "", "mineral_composition": ""}`, 19 | }, 20 | { 21 | taskType: `compile_mining_viability_report`, 22 | agentType: `mining_viability_report_compiler` satisfies AgentType, 23 | description: `Compile a comprehensive mining viability report by integrating the provided mineral composition data and cross-referenced orbital mechanics data. Analyze and synthesize the information to produce a detailed report that includes an assessment of the mining potential, technical challenges, and recommendations based on the integrated data.`, 24 | taskConfigInput: `{"mineral_composition_data": "", "cross_referenced_data": ""}`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/deep-sea-exploration/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 4 | import agentConfigFixtures from "./agent-config.js"; 5 | 6 | type AgentType = FixtureName; 7 | const ENTRIES = [ 8 | { 9 | taskType: `sonar_mapping_underwater_terrain`, 10 | agentType: `underwater_terrain_mapper` satisfies AgentType, 11 | description: `Conduct sonar mapping to identify underwater terrain features in the specified using the given and . Return the terrain sonar data as output.`, 12 | taskConfigInput: `{"zone_name": "", "scan_resolution": "", "depth_range": ""}`, 13 | }, 14 | { 15 | taskType: `integrated_sonar_mapping`, 16 | agentType: `integrated_sonar_mapper` satisfies AgentType, 17 | description: `Conduct integrated sonar mapping to identify both underwater terrain features and marine life in the specified using the given , , , and . Return the integrated terrain and biological sonar data as output.`, 18 | taskConfigInput: `{"zone_name": "", "scan_resolution": "", "depth_range": "", "bio_frequency_range": "", "organism_filter": ""}`, 19 | }, 20 | { 21 | taskType: `generate_comprehensive_comparison_report`, 22 | agentType: `comprehensive_exploration_report_generator` satisfies AgentType, 23 | description: `Generate a comprehensive exploration report by integrating and comparing terrain and biological sonar data from multiple zones. Use the provided sonar data to analyze and compare the zones, and return a structured exploration report as output.`, 24 | taskConfigInput: `{"sonar_data": ["", "..."]}`, 25 | }, 26 | ] as const satisfies TaskConfigMinimal[]; 27 | 28 | export default createFixtures( 29 | addTaskConfigMissingAttrs(ENTRIES), 30 | ({ taskType }) => taskType, 31 | ); 32 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/task-initializer/agent-config-initializer/agent-instructions-builder/__tests__/helpers/create-example-input.ts: -------------------------------------------------------------------------------- 1 | import { WorkflowComposeFixture } from "@/agents/supervisor/workflow/fixtures/base/workflow-compose-fixtures.js"; 2 | import { 3 | BaseCreateExampleInput, 4 | ExampleInput, 5 | } from "@/agents/supervisor/workflow/fixtures/helpers/create-example.js"; 6 | import { prepareDataForWorkflowStep } from "@/agents/supervisor/workflow/fixtures/helpers/prepare-resources.js"; 7 | import { TaskStep } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/dto.js"; 8 | import { protocol } from "../../protocol.js"; 9 | import { AgentConfigTinyDraft } from "../../../dto.js"; 10 | import { assertTaskStepResourceType } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/helpers/assert.js"; 11 | 12 | export interface CreateExampleInput 13 | extends BaseCreateExampleInput { 14 | instructions: string; 15 | } 16 | 17 | export interface InstructionsExampleInput 18 | extends ExampleInput { 19 | taskStep: TaskStep; 20 | agentConfigDraft: AgentConfigTinyDraft; 21 | } 22 | 23 | export function createExampleInput( 24 | input: CreateExampleInput, 25 | ): InstructionsExampleInput { 26 | const { step, fixtures, subtitle, note, instructions } = input; 27 | 28 | const fullSubtitle = `${subtitle ?? fixtures.title}${note ? ` (${note})` : ""}`; 29 | 30 | const stepNo = fixtures.taskSteps.stepNo(step); 31 | 32 | const { resources, previousSteps, taskStep } = prepareDataForWorkflowStep( 33 | fixtures, 34 | "taskConfigInitializer", 35 | stepNo, 36 | ); 37 | 38 | assertTaskStepResourceType(taskStep, "agent"); 39 | 40 | const agentConfigDraft = { 41 | agentType: taskStep.resource.agent.agentType, 42 | description: taskStep.resource.agent.description, 43 | tools: taskStep.resource.agent.tools, 44 | } satisfies AgentConfigTinyDraft; 45 | 46 | return { 47 | title: "INSTRUCTIONS", 48 | subtitle: fullSubtitle, 49 | user: taskStep.step, 50 | context: { 51 | previousSteps, 52 | resources, 53 | }, 54 | agentConfigDraft, 55 | taskStep, 56 | example: { 57 | INSTRUCTIONS: instructions, 58 | }, 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/base/runnable.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from "beeai-framework"; 2 | import { Context } from "./context.js"; 3 | import { AgentIdValue } from "@/agents/registry/dto.js"; 4 | import { AgentUpdateCallback } from "../dto.js"; 5 | import { SupervisorWorkflowStateLogger } from "../state/logger.js"; 6 | 7 | export abstract class Runnable { 8 | protected logger: Logger; 9 | protected stateLogger: SupervisorWorkflowStateLogger; 10 | private _agentId: string; 11 | private _name: string; 12 | 13 | get agentId() { 14 | return this._agentId; 15 | } 16 | 17 | get name() { 18 | return this._name; 19 | } 20 | 21 | constructor(logger: Logger, agentId: AgentIdValue) { 22 | this._name = this.constructor.name; 23 | this.logger = logger.child({ 24 | name: this._name, 25 | }); 26 | this._agentId = agentId; 27 | this.stateLogger = SupervisorWorkflowStateLogger.getInstance(); 28 | } 29 | 30 | abstract logStateInput( 31 | input: TInput, 32 | state: SupervisorWorkflowStateLogger, 33 | ctx: Context, 34 | ): Promise; 35 | 36 | abstract logStateOutput( 37 | output: TOutput, 38 | state: SupervisorWorkflowStateLogger, 39 | ctx: Context, 40 | ): Promise; 41 | 42 | // FIXME: Replace with decorator for logging state start and end 43 | async run(input: TInput, ctx: Context): Promise { 44 | await this.logStateInput(input, this.stateLogger, ctx); 45 | const output = await this._run(input, ctx); 46 | await this.logStateOutput(output, this.stateLogger, ctx); 47 | return output; 48 | } 49 | protected abstract _run(input: TInput, ctx: Context): Promise; 50 | 51 | protected handleOnUpdate( 52 | onUpdate: AgentUpdateCallback, 53 | input: 54 | | { type?: string; value: string; payload?: string | { toJson: any } } 55 | | string, 56 | ) { 57 | let value; 58 | if (typeof input === "string") { 59 | value = `${this._name} > ${input}$`; 60 | } else { 61 | value = `${this._name}${input.type ? `[${input.type}] > ` : " "}${input.value}`; 62 | 63 | if (input.payload) { 64 | value += `\n${typeof input.payload === "string" ? input.payload : JSON.stringify(input.payload.toJson, null, " ")}`; 65 | } 66 | } 67 | 68 | onUpdate(this._agentId, value); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/f1-grand-prix/index.ts: -------------------------------------------------------------------------------- 1 | import agentsFixtures from "./agent-config.js"; 2 | import tasksFixtures from "./task-config.js"; 3 | import taskStepsFixtures from "./task-step.js"; 4 | import taskRunsFixtures from "./task-run.js"; 5 | import toolsFixtures from "./tools.js"; 6 | import { 7 | ChoiceExplanations, 8 | WorkflowComposeFixture, 9 | } from "../../base/workflow-compose-fixtures.js"; 10 | 11 | const title = `List Last 2 Grands Prix of Last F1 Season`; 12 | 13 | const prompt = `Can you list last 2 grands prix of last F1 season?`; 14 | 15 | const choiceExplanations = { 16 | requestHandler: `TBD`, 17 | problemDecomposer: `TBD`, 18 | steps: [ 19 | { 20 | no: 1, 21 | agentConfig: `TBD`, 22 | taskConfig: `TBD`, 23 | taskRun: `TBD`, 24 | }, 25 | { 26 | no: 2, 27 | agentConfig: `TBD`, 28 | taskConfig: `TBD`, 29 | taskRun: `TBD`, 30 | }, 31 | { 32 | no: 3, 33 | agentConfig: `TBD`, 34 | taskConfig: `TBD`, 35 | taskRun: `TBD`, 36 | }, 37 | { 38 | no: 4, 39 | agentConfig: `TBD`, 40 | taskConfig: `TBD`, 41 | taskRun: `TBD`, 42 | }, 43 | { 44 | no: 5, 45 | agentConfig: `TBD`, 46 | taskConfig: `TBD`, 47 | taskRun: `TBD`, 48 | }, 49 | { 50 | no: 6, 51 | agentConfig: `TBD`, 52 | taskConfig: `TBD`, 53 | taskRun: `TBD`, 54 | }, 55 | ], 56 | } satisfies ChoiceExplanations; 57 | 58 | export const requestHandlerOutput = `{ 59 | "requestType": "sports_info", 60 | "primaryGoal": "List the last 2 Grands Prix of the last F1 season", 61 | "userParameters": { 62 | "sport": "F1", 63 | "season": "Last", 64 | "events": "last 2 Grands Prix" 65 | }, 66 | "requiredComponents": [ 67 | "identify the last F1 season", 68 | "retrieve the schedule of the last F1 season", 69 | "extract the last 2 Grands Prix" 70 | ], 71 | "expectedDeliverables": "Names and dates of the last 2 Grands Prix of the last F1 season" 72 | }`; 73 | 74 | const fixtures = new WorkflowComposeFixture( 75 | title, 76 | prompt, 77 | choiceExplanations, 78 | requestHandlerOutput, 79 | taskStepsFixtures, 80 | toolsFixtures, 81 | agentsFixtures, 82 | tasksFixtures, 83 | taskRunsFixtures, 84 | ); 85 | 86 | export default fixtures; 87 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/problem-decomposer/__tests__/helpers/create-example-input.ts: -------------------------------------------------------------------------------- 1 | import { WorkflowComposeFixture } from "@/agents/supervisor/workflow/fixtures/base/workflow-compose-fixtures.js"; 2 | import { unwrapTaskStepWithToolsOrLLM } from "@/agents/supervisor/workflow/fixtures/helpers/unwrap-task-step.js"; 3 | import { Resources } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 4 | import { TaskStep } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/dto.js"; 5 | import { TaskStepMapper } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/task-step-mapper.js"; 6 | import * as laml from "@/laml/index.js"; 7 | import { protocol } from "../../protocol.js"; 8 | 9 | export interface ExampleInput { 10 | title: string; 11 | subtitle: string; 12 | user: string; 13 | context: { 14 | previousSteps: TaskStep[]; 15 | resources: Resources; 16 | }; 17 | example: laml.ProtocolResult; 18 | } 19 | 20 | export function createExampleInput({ 21 | scenario, 22 | subtitle, 23 | note, 24 | fixtures, 25 | }: { 26 | scenario: "STEP_SEQUENCE"; 27 | subtitle?: string; 28 | note?: string; 29 | fixtures: F; 30 | }) { 31 | switch (scenario) { 32 | case "STEP_SEQUENCE": { 33 | const user = fixtures.requestHandlerOutput; 34 | const tools = fixtures.tools.values; 35 | const step_sequence = fixtures.taskSteps.values 36 | .map(unwrapTaskStepWithToolsOrLLM) 37 | .map(TaskStepMapper.format); 38 | const fullSubtitle = `${subtitle ?? fixtures.title}${note ? ` (${note})` : ""}`; 39 | return { 40 | title: scenario, 41 | subtitle: fullSubtitle, 42 | user, 43 | context: { 44 | previousSteps: [], 45 | resources: { 46 | tools, 47 | agents: [], // Unused in this step 48 | tasks: [], // Unused in this step 49 | taskRuns: [], // Unused in this step 50 | }, 51 | }, 52 | example: { 53 | RESPONSE_CHOICE_EXPLANATION: 54 | fixtures.choiceExplanations.problemDecomposer, 55 | RESPONSE_TYPE: "STEP_SEQUENCE", 56 | RESPONSE_STEP_SEQUENCE: { 57 | step_sequence, 58 | }, 59 | }, 60 | } satisfies ExampleInput; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/tools/openf1/get-grand-prix-detail.ts: -------------------------------------------------------------------------------- 1 | import { Tool, ToolEmitter, ToolInput } from "beeai-framework"; 2 | import { Emitter } from "beeai-framework/emitter/emitter"; 3 | import { parseISO } from "date-fns"; 4 | import { z } from "zod"; 5 | import { 6 | FormattedToolOutput, 7 | formatToolDescription, 8 | formatToolOutput, 9 | } from "../helpers/tool-format.js"; 10 | import { GrandPrixDetail } from "./dto.js"; 11 | import { OpenF1Service } from "./service.js"; 12 | 13 | export type OutputObject = GrandPrixDetail; 14 | export type GetGrandPrixDetailToolOutput = FormattedToolOutput; 15 | 16 | const example = { 17 | grand_prix_id: 1219, 18 | name: "Singapore Grand Prix", 19 | official_name: "FORMULA 1 SINGAPORE AIRLINES SINGAPORE GRAND PRIX 2023", 20 | circuit: "Singapore", 21 | date: parseISO("2023-09-15T09:30:00+00:00"), 22 | country: "SGP", 23 | location: "Marina Bay", 24 | sessions: [ 25 | { 26 | session_id: 9158, 27 | session_name: "Practice 1", 28 | session_type: "Practice", 29 | }, 30 | { 31 | session_id: 9159, 32 | session_name: "Practice 2", 33 | session_type: "Practice", 34 | }, 35 | { 36 | session_id: 9160, 37 | session_name: "Qualifying", 38 | session_type: "Qualifying", 39 | }, 40 | { 41 | session_id: 9161, 42 | session_name: "Race", 43 | session_type: "Race", 44 | }, 45 | ], 46 | } satisfies OutputObject; 47 | 48 | export class GetGrandPrixDetailTool extends Tool { 49 | name = "f1_get_grand_prix_detail"; 50 | description = formatToolDescription({ 51 | description: 52 | "Provides detailed information about a specific F1 Grand Prix, including its date, location, and results.", 53 | example, 54 | }); 55 | 56 | static { 57 | this.register(); 58 | } 59 | 60 | public readonly emitter: ToolEmitter< 61 | ToolInput, 62 | GetGrandPrixDetailToolOutput 63 | > = Emitter.root.child({ 64 | namespace: ["tool", "f1_get_grand_prix_detail"], 65 | creator: this, 66 | }); 67 | 68 | inputSchema() { 69 | return z.object({ 70 | grand_prix_id: z.number(), 71 | }); 72 | } 73 | 74 | protected async _run(input: ToolInput) { 75 | const output = await OpenF1Service.getInstance().getGrandPrixDetail( 76 | input.grand_prix_id, 77 | ); 78 | return formatToolOutput(output); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/resources/utils.ts: -------------------------------------------------------------------------------- 1 | import { ServiceLocator } from "@/utils/service-locator.js"; 2 | import { Resources } from "./dto.js"; 3 | import { AgentRegistry } from "@/agents/registry/registry.js"; 4 | import { 5 | AgentConfig, 6 | AgentIdValue, 7 | AgentKindEnum, 8 | } from "@/agents/registry/dto.js"; 9 | import { TaskManager } from "@/tasks/manager/manager.js"; 10 | import { clone } from "remeda"; 11 | import { TaskConfig } from "@/tasks/manager/dto.js"; 12 | 13 | export function collectResources( 14 | agentKind: AgentKindEnum, 15 | actingAgentId: AgentIdValue, 16 | ): Resources { 17 | const agentRegistry = ServiceLocator.getInstance().get(AgentRegistry); 18 | const tools = Array.from( 19 | agentRegistry.getToolsFactory(agentKind).availableTools.values(), 20 | ); 21 | 22 | const agents = agentRegistry.getAgentConfigs({ 23 | kind: agentKind, 24 | }); 25 | 26 | const taskManager = ServiceLocator.getInstance().get(TaskManager); 27 | const tasks = taskManager.getTaskConfigs(actingAgentId, { 28 | kind: agentKind, 29 | }); 30 | 31 | const taskRuns = taskManager.getTaskRuns(actingAgentId, { 32 | taskKind: agentKind, 33 | }); 34 | 35 | return { 36 | tools, 37 | agents, 38 | tasks, 39 | taskRuns, 40 | } satisfies Resources; 41 | } 42 | 43 | export function extendResources( 44 | resources: Resources, 45 | newResources: Partial, 46 | ): Resources { 47 | return clone({ 48 | tools: [...resources.tools, ...(newResources.tools ?? [])], 49 | agents: [...resources.agents, ...(newResources.agents ?? [])], 50 | tasks: [...resources.tasks, ...(newResources.tasks ?? [])], 51 | taskRuns: [...resources.taskRuns, ...(newResources.taskRuns ?? [])], 52 | }); 53 | } 54 | 55 | export function replaceAgentByAgentTypeInResources( 56 | resources: Resources, 57 | newAgent: AgentConfig, 58 | ): Resources { 59 | const agents = resources.agents.map((agent) => 60 | agent.agentType === newAgent.agentType ? newAgent : agent, 61 | ); 62 | return clone({ 63 | ...resources, 64 | agents, 65 | }); 66 | } 67 | 68 | export function replaceTaskByTaskTypeInResources( 69 | resources: Resources, 70 | newTask: TaskConfig, 71 | ): Resources { 72 | const tasks = resources.tasks.map((task) => 73 | task.taskType === newTask.taskType ? newTask : task, 74 | ); 75 | return clone({ 76 | ...resources, 77 | tasks, 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/medieval-charter-digitisation/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import agentConfigFixtures from "./agent-config.js"; 3 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 4 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 5 | 6 | type AgentType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "scan_documents_high_res", 11 | agentType: "document_scanner" satisfies AgentType, 12 | description: 13 | "Scan each document identified by to produce high-resolution images and return URLs of the scanned images.", 14 | taskConfigInput: `{"document_ids": ["", "..."]}`, 15 | }, 16 | { 17 | taskType: "extract_text_from_images_latin_script", 18 | agentType: "ocr_latin_text_extractor" satisfies AgentType, 19 | description: 20 | "Extract text from each image URL provided using OCR tuned for Latin script, ensuring the text is in Latin script. Return the extracted text along with confidence scores for each image.`", 21 | taskConfigInput: `{"image_urls": ["", "..."], "language_hint": "lat"}`, 22 | }, 23 | { 24 | taskType: "verify_language_of_extracted_text", 25 | agentType: "language_verification" satisfies AgentType, 26 | description: 27 | "Verify the language of each provided text snippet to ensure it is in Latin script. Return the language detection results, including the detected language and confidence score for each text snippet.", 28 | taskConfigInput: `{"text_snippets": ["", "..."]}`, 29 | }, 30 | { 31 | taskType: "load_verified_text_into_vector_search", 32 | agentType: "vector_text_ingestor" satisfies AgentType, 33 | description: 34 | "Ingest the provided verified text into a vector search system by chunking the text into default sizes of 1000 characters, embedding the chunks, and storing them with provenance tags. Return a confirmation of successful loading for each document ID.`", 35 | taskConfigInput: `{"document_ids": ["", "..."], "verified_text": ["", "..."], "chunk_size": 1000}`, 36 | }, 37 | ] as const satisfies TaskConfigMinimal[]; 38 | 39 | export default createFixtures( 40 | addTaskConfigMissingAttrs(ENTRIES), 41 | ({ taskType }) => taskType, 42 | ); 43 | -------------------------------------------------------------------------------- /src/utils/zod.ts: -------------------------------------------------------------------------------- 1 | // utils/crossProductEnum.ts 2 | import { z } from "zod"; 3 | 4 | /** 5 | * Type‑level cartesian product of two Zod enums in the form 6 | * `"${Left}_${Right}"`. 7 | * 8 | * @template L A {@link z.ZodEnum} whose literal values form the **left** half 9 | * @template R A {@link z.ZodEnum} whose literal values form the **right** half 10 | * 11 | * `L["_output"]` / `R["_output"]` expose the literal union of each enum’s 12 | * values at the type level; the template‑literal type then combines them. 13 | * 14 | * @example 15 | * ```ts 16 | * // Given NodeKindEnum = z.enum(["foo", "bar"]) 17 | * // PhaseEnum = z.enum(["start", "end"]) 18 | * type T = Cross; 19 | * // → "foo_start" | "foo_end" | "bar_start" | "bar_end" 20 | * ``` 21 | */ 22 | export type Cross< 23 | L extends z.ZodEnum<[string, ...string[]]>, 24 | R extends z.ZodEnum<[string, ...string[]]>, 25 | > = `${L["_output"]}_${R["_output"]}`; 26 | 27 | /** 28 | * Runtime + compile‑time helper that returns a new Zod enum whose members are 29 | * every `${left}_${right}` combination of two input enums. 30 | * 31 | * The resulting enum keeps **exact** literal typing, so `z.infer` produces 32 | * the precise union and `.parse` rejects stray strings. 33 | * 34 | * @param left Zod enum providing the left‑hand fragments 35 | * @param right Zod enum providing the right‑hand fragments 36 | * 37 | * @returns A `z.ZodEnum` instance of the cartesian product, fully typed. 38 | * 39 | * @example 40 | * ```ts 41 | * const NodePhaseEnum = crossProductEnum(NodeKindEnum, PhaseEnum); 42 | * 43 | * type NodePhase = z.infer; 44 | * // "foo_start" | "foo_end" | "bar_start" | "bar_end" 45 | * 46 | * NodePhaseEnum.parse("foo_start"); // ✅ OK 47 | * NodePhaseEnum.parse("foo"); // ❌ ZodError 48 | * ``` 49 | */ 50 | export function crossProductEnum< 51 | L extends z.ZodEnum<[string, ...string[]]>, 52 | R extends z.ZodEnum<[string, ...string[]]>, 53 | >(left: L, right: R) { 54 | /* ────────────────────────────────────────────────────────────────── 55 | * Build the runtime list: ["foo_start", "foo_end", ...] */ 56 | const values = left.options.flatMap((l) => 57 | right.options.map((r) => `${l}_${r}`), 58 | ); 59 | 60 | /* ────────────────────────────────────────────────────────────────── 61 | * Zod’s `enum()` requires a **non‑empty tuple** of string literals 62 | * at compile time, so assert that `values` has that shape. */ 63 | return z.enum(values as [Cross, ...Cross[]]); 64 | } 65 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/helpers/unwrap-task-step.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TaskStepAgentResource, 3 | TaskStepLLMResource, 4 | TaskStepResource, 5 | TaskStepRunResource, 6 | TaskStepTaskResource, 7 | TaskStepToolsResource, 8 | } from "../../workflow-composer/helpers/task-step/dto.js"; 9 | import { TaskStepWithVariousResource } from "../base/resource-fixtures.js"; 10 | 11 | type ResourceOf = Extract< 12 | TaskStepResource, 13 | { type: T } 14 | >; 15 | 16 | export function unwrapTaskStepWithToolsOrLLM( 17 | step: TaskStepWithVariousResource, 18 | ): Omit & { 19 | resource: TaskStepLLMResource | TaskStepToolsResource; 20 | } { 21 | if (step.resource.values.some((r) => r.type === "llm")) { 22 | return unwrapTaskStep(step, "llm"); 23 | } 24 | return unwrapTaskStepWithTools(step); 25 | } 26 | 27 | export function unwrapTaskStepWithTools( 28 | step: TaskStepWithVariousResource, 29 | ): Omit & { 30 | resource: TaskStepToolsResource; 31 | } { 32 | return unwrapTaskStep(step, "tools"); 33 | } 34 | export function unwrapTaskStepWithLLM(step: TaskStepWithVariousResource): Omit< 35 | TaskStepWithVariousResource, 36 | "resource" 37 | > & { 38 | resource: TaskStepLLMResource; 39 | } { 40 | return unwrapTaskStep(step, "llm"); 41 | } 42 | 43 | export function unwrapTaskStepWithAgent( 44 | step: TaskStepWithVariousResource, 45 | ): Omit & { 46 | resource: TaskStepAgentResource; 47 | } { 48 | return unwrapTaskStep(step, "agent"); 49 | } 50 | export function unwrapTaskStepWithTask(step: TaskStepWithVariousResource): Omit< 51 | TaskStepWithVariousResource, 52 | "resource" 53 | > & { 54 | resource: TaskStepTaskResource; 55 | } { 56 | return unwrapTaskStep(step, "task"); 57 | } 58 | export function unwrapTaskStepWithTaskRun( 59 | step: TaskStepWithVariousResource, 60 | ): Omit & { 61 | resource: TaskStepRunResource; 62 | } { 63 | return unwrapTaskStep(step, "task_run"); 64 | } 65 | export function unwrapTaskStep( 66 | step: TaskStepWithVariousResource, 67 | key: K, 68 | ): Omit & { resource: ResourceOf } { 69 | const res = step.resource.get(key as K) as ResourceOf; 70 | 71 | if (!res) { 72 | throw new Error( 73 | `Task step "${step.step}" does not contain a resource of type "${key}"`, 74 | ); 75 | } 76 | return { ...step, resource: res }; 77 | } 78 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/__test__/boston-trip/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "../../../workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | { 6 | toolName: "tavily_search_api", 7 | description: 8 | "Perform fast and relevant web searches, returning concise summaries of top-ranked results.", 9 | toolInput: 10 | '{"query":"","numResults":}', 11 | }, 12 | { 13 | toolName: "tavily_page_extract", 14 | description: 15 | "A focused content-retrieval endpoint that fetches the full readable text (and, where available, metadata such as title, author, publish date, alt-text, and canonical URL) from one or more specific webpages you already know the addresses of; invoke it after a search—or whenever the user supplies or requests exact URLs—when you must quote, summarize, fact-check, extract tables/code/snippets, or reason over details that are not reliably captured in snippets alone, while skipping it if (a) the question can be answered from your own knowledge or search snippets, (b) the site is pay-walled, requires login, or hosts dynamic content that scraping would miss, or (c) the user forbids browsing; call it with a JSON object whose urls field is a list of absolute URLs (add optional max_chars, include_images, or selector keys if supported) and then parse the returned plain text or structured data, keeping network calls minimal by batching related URLs, respecting copyright, and citing any extracted material.", 16 | toolInput: 17 | '{"urls":[""],"max_chars":,"include_images":,"selector":""}', 18 | }, 19 | { 20 | toolName: "historical_sites_search_api", 21 | description: 22 | "Purpose-built lookup for *place-based* heritage queries. Give it any neighborhood, city, or lat/long (e.g., “Back Bay”) and it returns structured JSON for each matching historic or archaeological site: official name, era, brief significance, coordinates, jurisdiction, and citation links from authoritative registers (UNESCO, U.S. National Register, state inventories, etc.). **Use this tool whenever the user wants to *find, list, or map* historic sites at a location—no generic web search needed.**", 23 | toolInput: '{"location":""}', 24 | }, 25 | ] as const satisfies AgentAvailableTool[]; 26 | 27 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 28 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/micro-grid-load-balancing/task-config.ts: -------------------------------------------------------------------------------- 1 | import { TaskConfigMinimal } from "@/agents/supervisor/workflow/workflow-composer/task-initializer/task-config-initializer/dto.js"; 2 | import agentConfigFixtures from "./agent-config.js"; 3 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 4 | import { addTaskConfigMissingAttrs } from "../../../helpers/add-missing-config-attrs.js"; 5 | 6 | type AgentType = FixtureName; 7 | 8 | const ENTRIES = [ 9 | { 10 | taskType: "forecast_electricity_demand", 11 | agentType: "electricity_demand_forecaster" satisfies AgentType, 12 | description: 13 | "Forecast electricity demand for each specified city block starting from for 15-minute intervals using the demand_forecast_api.", 14 | taskConfigInput: `{"blockIds": ["", "..."], "start_time": "", "periods": ""}`, 15 | }, 16 | { 17 | taskType: "forecast_solar_battery", 18 | agentType: "solar_battery_forecaster" satisfies AgentType, 19 | description: 20 | "Forecast solar generation and battery state-of-charge for each specified site starting from for 15-minute intervals using the solar_battery_forecast_api.", 21 | taskConfigInput: `{"siteIds": ["", "..."], "start_time": "", "periods": ""}`, 22 | }, 23 | { 24 | taskType: "optimize_dispatch_schedule", 25 | agentType: "dispatch_schedule_optimizer" satisfies AgentType, 26 | description: 27 | "Generate an optimized dispatch schedule that meets the forecasted electricity demand using solar, battery, and EV resources while ensuring frequency deviation stays within the specified limits.`", 28 | taskConfigInput: `{"demand_forecast": "", "solar_battery_forecasts": "", "constraint": {"freqDeviationHz": ""}}`, 29 | }, 30 | { 31 | taskType: "send_control_vectors", 32 | agentType: "dispatch_command_sender" satisfies AgentType, 33 | description: 34 | "Send control vectors to implement the optimized dispatch schedule using the dispatch_command_api and receive an acknowledgement of dispatch as confirmation of successful implementation.`", 35 | taskConfigInput: `{"control_vectors": ""}`, 36 | }, 37 | ] as const satisfies (TaskConfigMinimal & { 38 | agentType: AgentType; 39 | })[]; 40 | 41 | export default createFixtures( 42 | addTaskConfigMissingAttrs(ENTRIES), 43 | ({ taskType }) => taskType, 44 | ); 45 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/deep-sea-exploration/tools.ts: -------------------------------------------------------------------------------- 1 | import { AgentAvailableTool } from "@/agents/supervisor/workflow/workflow-composer/helpers/resources/dto.js"; 2 | import { createFixtures } from "../../../base/fixtures.js"; 3 | 4 | const ENTRIES = [ 5 | // Initial Tool (terrain mapping only) 6 | { 7 | toolName: "terrain_sonar_mapping_api", 8 | description: 9 | "Maps underwater terrain features using standard sonar pulses. Identifies depth contours, geological formations, seafloor topology, and underwater obstacles. Cannot detect biological entities or marine life.", 10 | toolInput: 11 | '{"zone_name":"","scan_resolution":"standard|high","depth_range":"shallow|deep|full"}', 12 | }, 13 | 14 | // Extension Tool (biological detection) 15 | { 16 | toolName: "biological_sonar_detector_api", 17 | description: 18 | "Detects and tracks marine life using specialized bio-acoustic sonar frequencies. Identifies fish schools, marine mammals, and other living organisms. Designed to work alongside terrain mapping for comprehensive zone analysis.", 19 | toolInput: 20 | '{"zone_name":"","bio_frequency_range":"low|medium|high","organism_filter":"fish|mammals|all"}', 21 | }, 22 | 23 | // Data Integration Tools 24 | { 25 | toolName: "sonar_data_integrator_api", 26 | description: 27 | "Integrates terrain and biological sonar data into unified underwater maps. Combines geological and biological information to create comprehensive zone profiles.", 28 | toolInput: 29 | '{"terrain_sonar_data":"","biological_sonar_data":"","integration_detail":"basic|comprehensive"}', 30 | }, 31 | 32 | { 33 | toolName: "zone_comparison_analyzer_api", 34 | description: 35 | "Compares multiple exploration zones across terrain and biological characteristics. Analyzes integrated sonar data to rank zones for exploration suitability.", 36 | toolInput: 37 | '{"zone_analyses":[""],"comparison_criteria":"terrain|biology|combined","ranking_priority":"exploration_value"}', 38 | }, 39 | 40 | // Reporting Tools 41 | { 42 | toolName: "submarine_exploration_reporter_api", 43 | description: 44 | "Generates structured exploration reports from integrated sonar analysis. Creates documentation suitable for mission planning and scientific review.", 45 | toolInput: 46 | '{"integrated_analysis":"","comparison_data":"","report_type":"mission_brief|scientific|comprehensive"}', 47 | }, 48 | ] as const satisfies AgentAvailableTool[]; 49 | 50 | export default createFixtures(ENTRIES, ({ toolName }) => toolName); 51 | -------------------------------------------------------------------------------- /src/workspaces/restore/restorable.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WorkspaceManager, 3 | WorkspaceResource, 4 | } from "@workspaces/manager/manager.js"; 5 | import { Logger } from "beeai-framework"; 6 | import { AgentIdValue } from "@/agents/registry/dto.js"; 7 | import { Disposable } from "@/utils/disposable.js"; 8 | 9 | export abstract class WorkspaceRestorable implements Disposable { 10 | protected _logger: Logger | null; 11 | protected _workspaceManager: WorkspaceManager | null; 12 | protected _resource: WorkspaceResource | null; 13 | protected resourceOwnerId: string; 14 | protected _disposed = false; 15 | 16 | protected get workspaceManager() { 17 | if (!this._workspaceManager) { 18 | throw new Error("Missing workspace manager"); 19 | } 20 | return this._workspaceManager; 21 | } 22 | 23 | protected get logger() { 24 | if (!this._logger) { 25 | throw new Error("Logger is missing"); 26 | } 27 | return this._logger; 28 | } 29 | 30 | protected get resource() { 31 | if (!this._resource) { 32 | throw new Error("Resource is missing"); 33 | } 34 | return this._resource; 35 | } 36 | 37 | get disposed() { 38 | return this._disposed; 39 | } 40 | 41 | constructor( 42 | path: readonly string[], 43 | resourceOwnerId: string, 44 | logger: Logger, 45 | ) { 46 | this._logger = logger.child({ 47 | name: this.constructor.name, 48 | }); 49 | this.resourceOwnerId = resourceOwnerId; 50 | this._workspaceManager = WorkspaceManager.getInstance(); 51 | this._resource = this._workspaceManager.registerResource( 52 | { 53 | kind: "file", 54 | path, 55 | }, 56 | resourceOwnerId, 57 | ); 58 | } 59 | 60 | persist(): void { 61 | const entities = this.getSerializedEntities(); 62 | this.workspaceManager.writeResource( 63 | this.resource.path, 64 | this.resourceOwnerId, 65 | entities, 66 | ); 67 | } 68 | 69 | protected abstract getSerializedEntities(): string; 70 | 71 | restore(actingAgentId: AgentIdValue): void { 72 | this.workspaceManager.readResource( 73 | this.resource.path, 74 | this.resourceOwnerId, 75 | (resource, content) => { 76 | this.restoreEntity(resource, content, actingAgentId); 77 | }, 78 | ); 79 | } 80 | 81 | protected abstract restoreEntity( 82 | resource: WorkspaceResource, 83 | line: string, 84 | actingAgentId: AgentIdValue, 85 | signal?: AbortSignal, 86 | ): void; 87 | 88 | dispose() { 89 | this._resource = null; 90 | this._workspaceManager = null; 91 | this._logger = null; 92 | this._disposed = true; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/tool.ts: -------------------------------------------------------------------------------- 1 | import { OperationResult } from "@/base/dto.js"; 2 | import { 3 | ActingAgentIdValueSchema, 4 | TaskRunIdValueSchema, 5 | } from "@/tasks/manager/dto.js"; 6 | import { TaskManager } from "@/tasks/manager/manager.js"; 7 | import { ServiceLocator } from "@/utils/service-locator.js"; 8 | import { Emitter } from "beeai-framework/emitter/emitter"; 9 | import { 10 | JSONToolOutput, 11 | Tool, 12 | ToolEmitter, 13 | ToolInput, 14 | } from "beeai-framework/tools/base"; 15 | import { z } from "zod"; 16 | 17 | export const TOOL_NAME = "task_run_starter"; 18 | 19 | export interface TaskRunStarterToolResult { 20 | method: "scheduleStartInteractionBlockingTaskRuns"; 21 | success: true; 22 | data: OperationResult[]; 23 | } 24 | 25 | export const ScheduleStartInteractionBlockingTaskRunsSchema = z 26 | .object({ 27 | method: z.literal("scheduleStartInteractionBlockingTaskRuns"), 28 | interactionTaskRunId: TaskRunIdValueSchema.describe( 29 | `The interaction task run ID`, 30 | ), 31 | actingAgentId: ActingAgentIdValueSchema, 32 | }) 33 | .describe( 34 | "Schedules starts of all blocking task runs that are blocked by the the interaction task run simultaneously.", 35 | ); 36 | 37 | export class TaskRunStarterTool extends Tool< 38 | JSONToolOutput 39 | > { 40 | name = TOOL_NAME; 41 | description = `The ${TOOL_NAME} provides functionalities to schedule start of a task run.`; 42 | 43 | static { 44 | this.register(); 45 | } 46 | 47 | public readonly emitter: ToolEmitter< 48 | ToolInput, 49 | JSONToolOutput 50 | > = Emitter.root.child({ 51 | namespace: ["tool", TOOL_NAME], 52 | creator: this, 53 | }); 54 | 55 | private get taskManager() { 56 | // Weak reference to the task manager 57 | return ServiceLocator.getInstance().get(TaskManager); 58 | } 59 | 60 | inputSchema() { 61 | return z.discriminatedUnion("method", [ 62 | ScheduleStartInteractionBlockingTaskRunsSchema, 63 | ]); 64 | } 65 | 66 | protected async _run(input: ToolInput) { 67 | let data: OperationResult[]; 68 | switch (input.method) { 69 | case "scheduleStartInteractionBlockingTaskRuns": { 70 | const { interactionTaskRunId, actingAgentId } = input; 71 | data = this.taskManager.scheduleStartInteractionBlockingTaskRuns( 72 | interactionTaskRunId, 73 | actingAgentId, 74 | ); 75 | break; 76 | } 77 | } 78 | return new JSONToolOutput({ 79 | method: input.method, 80 | success: true, 81 | data, 82 | } satisfies TaskRunStarterToolResult); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/workflow-composer/helpers/task-step/dto.ts: -------------------------------------------------------------------------------- 1 | import { AgentConfigSchema } from "@/agents/registry/dto.js"; 2 | import { TaskConfigSchema, TaskRunSchema } from "@/tasks/manager/dto.js"; 3 | import { z } from "zod"; 4 | 5 | export const TaskStepAssignedResourceEnumSchema = z.enum([ 6 | "tools", 7 | "llm", 8 | "agent", 9 | "task", 10 | "task_run", 11 | ]); 12 | export type TaskStepAssignedResourceEnum = z.infer< 13 | typeof TaskStepAssignedResourceEnumSchema 14 | >; 15 | 16 | export const TaskStepToolsResourceSchema = z.object({ 17 | type: z.literal(TaskStepAssignedResourceEnumSchema.Values.tools), 18 | tools: z.array(z.string()), 19 | }); 20 | export type TaskStepToolsResource = z.infer; 21 | 22 | export const TaskStepLLMResource = z.object({ 23 | type: z.literal(TaskStepAssignedResourceEnumSchema.Values.llm), 24 | }); 25 | export type TaskStepLLMResource = z.infer; 26 | 27 | export const TaskStepAgentResourceSchema = z.object({ 28 | type: z.literal(TaskStepAssignedResourceEnumSchema.Values.agent), 29 | agent: AgentConfigSchema, 30 | }); 31 | export type TaskStepAgentResource = z.infer; 32 | 33 | export const TaskStepTaskResourceSchema = z.object({ 34 | type: z.literal(TaskStepAssignedResourceEnumSchema.Values.task), 35 | task: TaskConfigSchema, 36 | }); 37 | export type TaskStepTaskResource = z.infer; 38 | 39 | export const TaskStepRunResourceSchema = z.object({ 40 | type: z.literal(TaskStepAssignedResourceEnumSchema.Values.task_run), 41 | taskRun: TaskRunSchema, 42 | }); 43 | export type TaskStepRunResource = z.infer; 44 | 45 | export const TaskStepResourceSchema = z.discriminatedUnion("type", [ 46 | TaskStepToolsResourceSchema, 47 | TaskStepLLMResource, 48 | TaskStepAgentResourceSchema, 49 | TaskStepTaskResourceSchema, 50 | TaskStepRunResourceSchema, 51 | ]); 52 | export type TaskStepResource = z.infer; 53 | 54 | export const TaskStepInputParameterSchema = z.object({ 55 | value: z.string(), 56 | assumed: z.boolean().optional(), 57 | dependencies: z.array(z.number()).optional(), 58 | }); 59 | export type TaskStepInputParameter = z.infer< 60 | typeof TaskStepInputParameterSchema 61 | >; 62 | 63 | export const TaskStepSchema = z.object({ 64 | no: z.number(), 65 | step: z.string(), 66 | inputs: z.array(TaskStepInputParameterSchema).optional(), 67 | output: z.string().optional(), 68 | resource: TaskStepResourceSchema, 69 | dependencies: z.array(z.number()).optional(), 70 | }); 71 | export type TaskStep = z.infer; 72 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/helpers/data-stepper.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { DataStepper } from "./data-stepper.js"; 3 | 4 | describe("Data Stepper ", () => { 5 | it("walktrough", () => { 6 | const stepper = new DataStepper([ 7 | { 8 | attr: "name", 9 | }, 10 | { 11 | attr: "addresses", 12 | array: true, 13 | flow: [ 14 | { 15 | attr: 0, 16 | flow: [ 17 | { 18 | attr: "street", 19 | }, 20 | { 21 | attr: "city", 22 | }, 23 | ], 24 | }, 25 | { 26 | attr: 1, 27 | flow: [ 28 | { 29 | attr: "street", 30 | }, 31 | { 32 | attr: "city", 33 | }, 34 | ], 35 | }, 36 | ], 37 | }, 38 | { 39 | attr: "details", 40 | flow: [ 41 | { 42 | attr: "age", 43 | }, 44 | ], 45 | }, 46 | ]); 47 | expect(stepper.currentPath).toEqual("name"); 48 | expect(stepper.isBackwardPossible).toBeFalsy(); 49 | expect(stepper.isForwardPossible).toBeTruthy(); 50 | 51 | stepper.backward(); 52 | expect(stepper.currentPath).toEqual("name"); 53 | 54 | stepper.forward(); 55 | expect(stepper.currentPath).toEqual("addresses[0].street"); 56 | 57 | stepper.forward(); 58 | expect(stepper.currentPath).toEqual("addresses[0].city"); 59 | 60 | stepper.forward(); 61 | expect(stepper.currentPath).toEqual("addresses[1].street"); 62 | 63 | stepper.forward(); 64 | expect(stepper.currentPath).toEqual("addresses[1].city"); 65 | expect(stepper.isForwardPossible).toBeTruthy(); 66 | 67 | stepper.forward(); 68 | expect(stepper.currentPath).toEqual("details.age"); 69 | expect(stepper.isForwardPossible).toBeFalsy(); 70 | 71 | stepper.forward(); 72 | expect(stepper.currentPath).toEqual("details.age"); 73 | 74 | stepper.backward(); 75 | expect(stepper.currentPath).toEqual("addresses[1].city"); 76 | 77 | stepper.backward(); 78 | expect(stepper.currentPath).toEqual("addresses[1].street"); 79 | 80 | stepper.backward(); 81 | expect(stepper.currentPath).toEqual("addresses[0].city"); 82 | 83 | stepper.backward(); 84 | expect(stepper.currentPath).toEqual("addresses[0].street"); 85 | expect(stepper.isForwardPossible).toBeTruthy(); 86 | 87 | stepper.backward(); 88 | expect(stepper.currentPath).toEqual("name"); 89 | expect(stepper.isBackwardPossible).toBeFalsy(); 90 | 91 | stepper.backward(); 92 | expect(stepper.currentPath).toEqual("name"); 93 | expect(stepper.isBackwardPossible).toBeFalsy(); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/showcases/feedback-sentiment-analysis/task-step.ts: -------------------------------------------------------------------------------- 1 | import { TaskStepMapper } from "@/agents/supervisor/workflow/workflow-composer/helpers/task-step/task-step-mapper.js"; 2 | import { createFixtures, FixtureName } from "../../../base/fixtures.js"; 3 | import { 4 | createResourceFixtures, 5 | TaskStepWithVariousResource, 6 | } from "../../../base/resource-fixtures.js"; 7 | import agentsFixtures from "./agent-config.js"; 8 | import tasksFixtures from "./task-config.js"; 9 | import taskRunsFixtures from "./task-run.js"; 10 | import toolsFixtures from "./tools.js"; 11 | 12 | type ToolName = FixtureName; 13 | 14 | const ENTRIES = [ 15 | { 16 | no: 1, 17 | step: `Load the customer feedback dataset`, 18 | ...TaskStepMapper.parseInputOutput( 19 | `input: datasetId: "cust-feedback-2025-06"; output: array of feedback texts`, 20 | ), 21 | resource: createResourceFixtures( 22 | { type: "tools", tools: ["customer_feedback_dataset_api"] as ToolName[] }, 23 | { 24 | type: "agent", 25 | agent: agentsFixtures.get(`customer_feedback_loader`), 26 | }, 27 | { 28 | type: "task", 29 | task: tasksFixtures.get(`load_customer_feedback_dataset`), 30 | }, 31 | { 32 | type: "task_run", 33 | taskRun: taskRunsFixtures.get("load_customer_feedback_dataset_1"), 34 | }, 35 | ), 36 | }, 37 | { 38 | no: 2, 39 | step: `Perform sentiment analysis on the feedback texts`, 40 | ...TaskStepMapper.parseInputOutput( 41 | `input: texts [from Step 1]; output: sentiment scores for each text`, 42 | ), 43 | resource: createResourceFixtures( 44 | { type: "tools", tools: ["sentiment_analysis_api"] as ToolName[] }, 45 | { type: "agent", agent: agentsFixtures.get(`sentiment_analysis_agent`) }, 46 | { type: "task", task: tasksFixtures.get(`perform_sentiment_analysis`) }, 47 | { 48 | type: "task_run", 49 | taskRun: taskRunsFixtures.get("perform_sentiment_analysis_1"), 50 | }, 51 | ), 52 | }, 53 | { 54 | no: 3, 55 | step: `Aggregate sentiment scores`, 56 | ...TaskStepMapper.parseInputOutput( 57 | `input: sentiment scores [from Step 2]; output: aggregated sentiment report`, 58 | ), 59 | resource: createResourceFixtures( 60 | { type: "llm" }, 61 | { type: "agent", agent: agentsFixtures.get(`sentiment_aggregator`) }, 62 | { type: "task", task: tasksFixtures.get(`aggregate_sentiment_scores`) }, 63 | { 64 | type: "task_run", 65 | taskRun: taskRunsFixtures.get("aggregate_sentiment_scores_1"), 66 | }, 67 | ), 68 | }, 69 | ] as const satisfies TaskStepWithVariousResource[]; 70 | 71 | const fixtures = createFixtures(ENTRIES, ({ step }) => step); 72 | export default fixtures; 73 | -------------------------------------------------------------------------------- /src/agents/supervisor/workflow/fixtures/prompt/README.md: -------------------------------------------------------------------------------- 1 | # Skills 2 | 3 | 1. Goal-and-requirement extraction Example: Given “Organise a three-day team 4 | off-site near Prague in late September, dog-friendly, <$4 000 total”, the 5 | agent pulls out dates, location radius, pet policy, and budget ceilings 6 | before planning. 7 | 8 | > > > SHOULD HAVE quality 9 | 10 | 2. Resource matching & capability mapping Example: The agent sees that a 11 | “soil-quality survey” request can be satisfied by an existing 12 | soil_sampler_droid agent plus a lab_analysis_api, so it chooses those instead 13 | of generic LLM steps. 14 | 15 | > > > SHOULD HAVE Matching tools 16 | 17 | 3. Task decomposition & ordered sequencing Example: Break “Digitise 12 medieval 18 | charters” into: (1) high-res scan, (2) OCR Latin script, (3) language 19 | detection, (4) human-readable output review, (5) archive to vector store. 20 | 21 | > > > SHOULD HAVE YES 22 | 23 | 4. Explicit dependency tracking Example: When drafting a crop-rotation plan, 24 | Step 4 (fertiliser schedule) is formally linked to yield predictions 25 | generated in Step 2. 26 | 27 | > > > SHOULD HAVE quality 28 | 29 | 5. Constraint satisfaction (cost, time, capacity) Example: Plan drone battery 30 | swaps so that each field pass stays under 25 min flight time and total 31 | batteries ≤ 6. 32 | 33 | > > > SHOULD HAVE YES 34 | 35 | 6. Parallelisation & critical-path optimisation Example: While 3-D scanning an 36 | artefact, the agent schedules a separate material-analysis request in 37 | parallel, shortening total turnaround. 38 | 39 | > > > SHOULD HAVE YES 40 | 41 | 7. Budget / resource-usage optimisation Example: Choose a rail-cargo route over 42 | air freight once the combined_cost_evaluator shows 18 % savings within 43 | deadline tolerances. 44 | 45 | > > > SHOULD HAVE ??? 46 | 47 | 8. Fallback / contingency planning Example: If weather_api predicts > 70 % rain 48 | on harvest day, automatically branch to an indoor grain-drying workflow. 49 | 50 | > > > Not support yet 51 | 52 | 9. Progress monitoring & re-planning Example: After Step 2 fails to locate a 53 | 4-star hotel under €180/night, dynamically re-plan to widen the search radius 54 | and re-evaluate cost. 55 | 56 | > > > Not support yet 57 | 58 | 10. Multi-objective trade-off balancing Example: Select a Formula 1 tyre 59 | strategy that minimises pit-stop count and maximises stint pace, displaying 60 | Pareto-front options. 61 | 62 | > > > SHOULD HAVE ??? 63 | 64 | 11. Provenance / audit-trail construction Example: Attach source URLs and 65 | intermediate file hashes for each dataset ingested into a paleoclimate 66 | meta-analysis. 67 | > > > YES 68 | 69 | Task decomposition Ordered sequencing Task dependencies Parallelisation 70 | Aggregation Conditions 71 | -------------------------------------------------------------------------------- /src/helpers/llm.ts: -------------------------------------------------------------------------------- 1 | import { AgentKindEnum } from "@/agents/registry/dto.js"; 2 | import { OpenAIChatModel } from "beeai-framework/adapters/openai/backend/chat"; 3 | import { OllamaChatModel } from "beeai-framework/adapters/ollama/backend/chat"; 4 | import { WatsonxChatModel } from "beeai-framework/adapters/watsonx/backend/chat"; 5 | import { ChatModel } from "beeai-framework/backend/chat"; 6 | import { getEnv, hasEnv, parseEnv } from "beeai-framework/internals/env"; 7 | import { z } from "zod"; 8 | 9 | export const Providers = { 10 | IBM_RITS: "ibm_rits", 11 | OLLAMA: "ollama", 12 | OPENAI: "openai", 13 | WATSONX: "watsonx", 14 | } as const; 15 | type Provider = (typeof Providers)[keyof typeof Providers]; 16 | 17 | const env = (name: string, type: AgentKindEnum) => 18 | `${name}_${type.toUpperCase()}`; 19 | 20 | export const LLMFactories: Record< 21 | Provider, 22 | (type: AgentKindEnum) => ChatModel 23 | > = { 24 | [Providers.IBM_RITS]: (type: AgentKindEnum) => 25 | new OpenAIChatModel( 26 | getEnv(env("IBM_RITS_MODEL", type)) || "", 27 | {}, 28 | { 29 | baseURL: getEnv(env("IBM_RITS_URL", type)), 30 | apiKey: getEnv("IBM_RITS_API_KEY"), 31 | headers: { 32 | RITS_API_KEY: getEnv("IBM_RITS_API_KEY") || "", 33 | }, 34 | }, 35 | ), 36 | [Providers.OPENAI]: (type: AgentKindEnum) => 37 | new OpenAIChatModel(getEnv(env("OPENAI_MODEL", type)) || "gpt-4o"), 38 | [Providers.OLLAMA]: (type: AgentKindEnum) => 39 | new OllamaChatModel(getEnv(env("OLLAMA_MODEL", type)) || "llama3.1:8b", { 40 | numCtx: 131072, 41 | }), 42 | [Providers.WATSONX]: (type: AgentKindEnum) => 43 | new WatsonxChatModel( 44 | getEnv(env("WATSONX_CHAT_MODEL", type)) || "llama3.1:8b", 45 | ), 46 | }; 47 | 48 | export function getChatLLM( 49 | type: AgentKindEnum, 50 | provider?: Provider, 51 | ): ChatModel { 52 | if (!provider) { 53 | const commonProvider = hasEnv("LLM_BACKEND") 54 | ? parseEnv("LLM_BACKEND", z.nativeEnum(Providers)) 55 | : null; 56 | 57 | const typeProvider = hasEnv(env("LLM_BACKEND", type)) 58 | ? parseEnv( 59 | env("LLM_BACKEND", type), 60 | z.nativeEnum(Providers), 61 | Providers.OPENAI, 62 | ) 63 | : null; 64 | 65 | if (typeProvider) { 66 | provider = typeProvider; 67 | } else if (commonProvider) { 68 | provider = commonProvider; 69 | } else { 70 | throw new Error( 71 | `One of env variables ('LLM_BACKEND' | '${env("LLM_BACKEND", type)}') has to be set`, 72 | ); 73 | } 74 | } 75 | 76 | const factory = LLMFactories[provider]; 77 | if (!factory) { 78 | throw new Error(`Provider "${provider}" not found.`); 79 | } 80 | 81 | const model = factory(type); 82 | model.config({ 83 | parameters: { 84 | temperature: 0, 85 | }, 86 | }); 87 | return model; 88 | } 89 | -------------------------------------------------------------------------------- /src/ui/chat-monitor/workflow-popup/workflow-explorer/screens/input-output-screen.ts: -------------------------------------------------------------------------------- 1 | import { ParentInput, ScreenInput } from "@/ui/base/monitor.js"; 2 | import { WorkflowScreen } from "./workflow-screen.js"; 3 | import { Logger } from "beeai-framework"; 4 | import { Style, StyledTextArea } from "../components/styled-text-area.js"; 5 | import blessed from "neo-blessed"; 6 | 7 | export class InputOutputScreen extends WorkflowScreen { 8 | private _input?: string; 9 | private inputTitleElement: blessed.Widgets.TextElement; 10 | private inputTextArea: StyledTextArea; 11 | private _output?: string; 12 | private outputTextArea: StyledTextArea; 13 | 14 | constructor(arg: ParentInput | ScreenInput, logger: Logger) { 15 | super(arg, logger); 16 | 17 | this.inputTitleElement = blessed.text({ 18 | parent: this.parent.element, 19 | top: 0, 20 | left: 0, 21 | width: "100%-2", 22 | height: 1, 23 | content: "Input:", 24 | style: { 25 | fg: "white", 26 | bold: true, 27 | }, 28 | }); 29 | this.inputTextArea = new StyledTextArea( 30 | this.parent.element, 31 | Style.USER_INPUT, 32 | ); 33 | this.inputTextArea.element.top = 1; 34 | this.inputTextArea.element.left = 0; 35 | this.inputTextArea.element.width = "100%-2"; 36 | this.inputTextArea.element.height = "50%-2"; 37 | 38 | this.outputTextArea = new StyledTextArea( 39 | this.parent.element, 40 | Style.USER_OUTPUT, 41 | ); 42 | this.outputTextArea.element.top = "50%+1"; 43 | this.outputTextArea.element.left = 0; 44 | this.outputTextArea.element.width = "100%-2"; 45 | this.outputTextArea.element.height = "50%-2"; 46 | 47 | this.render(); 48 | } 49 | 50 | set input(value: string) { 51 | this._input = value; 52 | this.render(); 53 | } 54 | 55 | set output(value: string) { 56 | this._output = value; 57 | this.render(); 58 | } 59 | 60 | render(): void { 61 | if (!this._input) { 62 | this.inputTextArea.element.hide(); 63 | this.inputTitleElement.hide(); 64 | } else { 65 | // this.inputTextArea.element.setContent(this._input); 66 | this.inputTextArea.element.setText("XXX"); 67 | this.inputTextArea.element.show(); 68 | this.inputTitleElement.show(); 69 | } 70 | 71 | if (!this._output) { 72 | this.outputTextArea.element.hide(); 73 | } else { 74 | // this.outputTextArea.element.setContent(this._output); 75 | this.inputTextArea.element.setText("YYY"); 76 | this.outputTextArea.element.show(); 77 | } 78 | 79 | this.screen.element.render(); 80 | } 81 | 82 | // get input() { 83 | // return this._input; 84 | // } 85 | 86 | // set input(value: string) { 87 | // this._input = value; 88 | // } 89 | 90 | // render(): string { 91 | // return `Input Screen: ${this._input}`; 92 | // } 93 | } 94 | --------------------------------------------------------------------------------