From c9577a613a0c6f69806970751fd3c3c441b81ab3 Mon Sep 17 00:00:00 2001 From: Dan Farrelly Date: Tue, 7 Jan 2025 17:54:09 -0500 Subject: [PATCH 1/7] Add basic server --- package.json | 2 +- pnpm-lock.yaml | 10 +++--- src/index.ts | 1 + src/network.ts | 26 ++++++++++------ src/server.ts | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 src/server.ts diff --git a/package.json b/package.json index c82bc18..3655563 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ }, "dependencies": { "express": "^4.21.1", - "inngest": "^3.28.0", + "inngest": "^3.29.0", "openai-zod-to-json-schema": "^1.0.3", "zod": "^3.23.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e2b57ce..eb641c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^4.21.1 version: 4.21.1 inngest: - specifier: ^3.28.0 - version: 3.28.0(express@4.21.1)(typescript@5.7.2) + specifier: ^3.29.0 + version: 3.29.0(express@4.21.1)(typescript@5.7.2) openai-zod-to-json-schema: specifier: ^1.0.3 version: 1.0.3(zod@3.23.8) @@ -866,8 +866,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inngest@3.28.0: - resolution: {integrity: sha512-7myzcPkX+A0PakUMwczIdDqt5lpHEgVqeK0N8IUcONjajlt/g1sk+yjZ88ER8SxoJWCFFoAV04NJdZN9ka1N1A==} + inngest@3.29.0: + resolution: {integrity: sha512-TQV9ce8nxNvj1nzOzWb197UHXHuXG3YHAkoSe0U5oqTAE7vs0a4HxQAzTivovsS9Z32f6FVx+m78Fov8sV0lQA==} engines: {node: '>=14'} peerDependencies: '@sveltejs/kit': '>=1.27.3' @@ -2477,7 +2477,7 @@ snapshots: inherits@2.0.4: {} - inngest@3.28.0(express@4.21.1)(typescript@5.7.2): + inngest@3.29.0(express@4.21.1)(typescript@5.7.2): dependencies: '@types/debug': 4.1.12 canonicalize: 1.0.8 diff --git a/src/index.ts b/src/index.ts index 42a9b2f..57d5b03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export * from "./agent"; export * from "./model"; export * from "./network"; export * from "./networkRun"; +export * from "./server"; export * from "./state"; export * from "./types"; export * from "./util"; diff --git a/src/network.ts b/src/network.ts index 8d6fbfa..8188c26 100644 --- a/src/network.ts +++ b/src/network.ts @@ -19,6 +19,11 @@ export const createNetwork = (opts: Network.Constructor) => new Network(opts); * Network represents a network of agents. */ export class Network { + /** + * The name for the system of agents + */ + name?: string; + /** * agents are all publicly available agents in the netwrok */ @@ -56,12 +61,14 @@ export class Network { protected _agents: Map; constructor({ + name, agents, defaultModel, maxIter, defaultState, defaultRouter, }: Network.Constructor) { + this.name = name; this.agents = new Map(); this._agents = new Map(); this.defaultModel = defaultModel; @@ -82,7 +89,7 @@ export class Network { } async availableAgents( - networkRun: NetworkRun = new NetworkRun(this, new State()), + networkRun: NetworkRun = new NetworkRun(this, new State()) ): Promise { const available: Agent[] = []; const all = Array.from(this.agents.values()); @@ -169,7 +176,7 @@ export const getDefaultRoutingAgent = () => { handler: ({ name }, { network }) => { if (!network) { throw new Error( - "The routing agent can only be used within a network of agents", + "The routing agent can only be used within a network of agents" ); } @@ -180,7 +187,7 @@ export const getDefaultRoutingAgent = () => { const agent = network.agents.get(name); if (agent === undefined) { throw new Error( - `The routing agent requested an agent that doesn't exist: ${name}`, + `The routing agent requested an agent that doesn't exist: ${name}` ); } @@ -196,7 +203,7 @@ export const getDefaultRoutingAgent = () => { system: async ({ network }): Promise => { if (!network) { throw new Error( - "The routing agent can only be used within a network of agents", + "The routing agent can only be used within a network of agents" ); } @@ -207,15 +214,15 @@ export const getDefaultRoutingAgent = () => { The following agents are available: ${agents - .map((a) => { - return ` + .map((a) => { + return ` ${a.name} ${a.description} ${JSON.stringify(Array.from(a.tools.values()))} `; - }) - .join("\n")} + }) + .join("\n")} Follow the set of instructions: @@ -234,6 +241,7 @@ Follow the set of instructions: export namespace Network { export type Constructor = { + name?: string; agents: Agent[]; defaultModel?: AiAdapter.Any; maxIter?: number; @@ -270,7 +278,7 @@ export namespace Network { * */ export type FnRouter = ( - args: Args, + args: Args ) => MaybePromise; export interface Args { diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..69b3d44 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,84 @@ +import { Inngest, type InngestFunction } from "inngest"; +import { createServer as createInngestServer } from "inngest/node"; + +import { type Network } from "./network"; +import { type Agent } from "./agent"; + +function slugify(str: string): string { + return str + .replace(/^\s+|\s+$/g, "") // trim leading/trailing white space + .toLowerCase() // convert string to lowercase + .replace(/[^a-z0-9 -]/g, "") // remove any non-alphanumeric characters + .replace(/\s+/g, "-") // replace spaces with hyphens + .replace(/-+/g, "-"); // remove consecutive hyphens +} + +/** + * Create a server to serve Agents and Networks as Inngest functions + * + * @example + * ```ts + * import { createServer, createAgent, createNetwork } from "@inngest/agent-kit"; + * + * const myAgent = createAgent(...); + * const myNetwork = createNetwork(...); + * const server = createServer({ + * agents: [myAgent], + * networks: [myNetworks], + * }); + * server.listen(3000) + * ``` + * + * @public + */ +export const createServer = ({ + networks = [], + agents = [], +}: { + networks?: Network[]; + agents?: Agent[]; +}) => { + const appId = "agent-kit"; + const inngest = new Inngest({ + id: appId, + }); + const functions: { [keyof: string]: InngestFunction.Any } = {}; + + for (const agent of agents) { + const slug = slugify(agent.name); + const id = `agent-${slug}`; + functions[id] = inngest.createFunction( + { id, name: agent.name }, + { event: `${appId}/${id}` }, + async ({ event }) => { + // eslint-disable-next-line + return agent.run(event.data.input); + } + ); + } + let networkIdx = 0; + for (const network of networks) { + networkIdx++; + const name = network.name ?? `My network #${networkIdx}`; + if (!network.name) { + console.warn( + `Network missing 'name' option. Created generic name: ${name}` + ); + } + const slug = slugify(name); + const id = `network-${slug}`; + functions[id] = inngest.createFunction( + { id, name }, + { event: `${appId}/${id}` }, + async ({ event }) => { + // eslint-disable-next-line + return network.run(event.data.input); + } + ); + } + + return createInngestServer({ + client: inngest, + functions: Object.values(functions), + }); +}; From 309d852759b3fa352a3bbc133057b0e1d5d15396 Mon Sep 17 00:00:00 2001 From: Dan Farrelly Date: Tue, 7 Jan 2025 18:02:06 -0500 Subject: [PATCH 2/7] Add changeset --- .changeset/pretty-planes-do.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/pretty-planes-do.md diff --git a/.changeset/pretty-planes-do.md b/.changeset/pretty-planes-do.md new file mode 100644 index 0000000..9e32a0c --- /dev/null +++ b/.changeset/pretty-planes-do.md @@ -0,0 +1,5 @@ +--- +"@inngest/agent-kit": minor +--- + +Add basic AgentKit server to serve agents and networks as Inngest functions for easy testing From 4658b8e0c2dee6c1b363bcb5ffe028df4d9239f1 Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:42:15 +0000 Subject: [PATCH 3/7] Move `createServer()` to a separate `@inngest/agent-kit/server` export --- package.json | 5 +++++ src/index.ts | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 01a6697..a573806 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,11 @@ "require": "./dist/index.js", "import": "./dist/index.js", "types": "./dist/index.d.ts" + }, + "./server": { + "require": "./dist/server.js", + "import": "./dist/server.js", + "types": "./dist/server.d.ts" } }, "dependencies": { diff --git a/src/index.ts b/src/index.ts index 57d5b03..42a9b2f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,6 @@ export * from "./agent"; export * from "./model"; export * from "./network"; export * from "./networkRun"; -export * from "./server"; export * from "./state"; export * from "./types"; export * from "./util"; From 4094cb84c084d93e9875ee6b03967052549cde0f Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:42:54 +0000 Subject: [PATCH 4/7] Add `name` as required to `Network` --- src/network.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network.ts b/src/network.ts index 8188c26..bc86be6 100644 --- a/src/network.ts +++ b/src/network.ts @@ -22,7 +22,7 @@ export class Network { /** * The name for the system of agents */ - name?: string; + name: string; /** * agents are all publicly available agents in the netwrok @@ -214,15 +214,15 @@ export const getDefaultRoutingAgent = () => { The following agents are available: ${agents - .map((a) => { - return ` + .map((a) => { + return ` ${a.name} ${a.description} ${JSON.stringify(Array.from(a.tools.values()))} `; - }) - .join("\n")} + }) + .join("\n")} Follow the set of instructions: @@ -241,7 +241,7 @@ Follow the set of instructions: export namespace Network { export type Constructor = { - name?: string; + name: string; agents: Agent[]; defaultModel?: AiAdapter.Any; maxIter?: number; From 3c0bcc26cca8c827f734f4144452f16341781397 Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:43:06 +0000 Subject: [PATCH 5/7] Add `description` to `Network` --- src/network.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network.ts b/src/network.ts index bc86be6..6087ad1 100644 --- a/src/network.ts +++ b/src/network.ts @@ -24,6 +24,8 @@ export class Network { */ name: string; + description?: string; + /** * agents are all publicly available agents in the netwrok */ @@ -62,6 +64,7 @@ export class Network { constructor({ name, + description, agents, defaultModel, maxIter, @@ -69,6 +72,7 @@ export class Network { defaultRouter, }: Network.Constructor) { this.name = name; + this.description = description; this.agents = new Map(); this._agents = new Map(); this.defaultModel = defaultModel; @@ -242,6 +246,7 @@ Follow the set of instructions: export namespace Network { export type Constructor = { name: string; + description?: string; agents: Agent[]; defaultModel?: AiAdapter.Any; maxIter?: number; From d922c19b006ea9eaaeb80eb797ec50fdced5be71 Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:43:27 +0000 Subject: [PATCH 6/7] Simplify `createServer()` if `Network#name` is present Also use `slugify` from `inngest` --- src/server.ts | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/server.ts b/src/server.ts index 69b3d44..b0e756a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,17 +1,7 @@ -import { Inngest, type InngestFunction } from "inngest"; +import { Inngest, slugify, type InngestFunction } from "inngest"; import { createServer as createInngestServer } from "inngest/node"; - -import { type Network } from "./network"; import { type Agent } from "./agent"; - -function slugify(str: string): string { - return str - .replace(/^\s+|\s+$/g, "") // trim leading/trailing white space - .toLowerCase() // convert string to lowercase - .replace(/[^a-z0-9 -]/g, "") // remove any non-alphanumeric characters - .replace(/\s+/g, "-") // replace spaces with hyphens - .replace(/-+/g, "-"); // remove consecutive hyphens -} +import { type Network } from "./network"; /** * Create a server to serve Agents and Networks as Inngest functions @@ -32,21 +22,22 @@ function slugify(str: string): string { * @public */ export const createServer = ({ + appId = "agent-kit", networks = [], agents = [], }: { + appId?: string; networks?: Network[]; agents?: Agent[]; }) => { - const appId = "agent-kit"; - const inngest = new Inngest({ - id: appId, - }); + const inngest = new Inngest({ id: appId }); + const functions: { [keyof: string]: InngestFunction.Any } = {}; for (const agent of agents) { const slug = slugify(agent.name); const id = `agent-${slug}`; + functions[id] = inngest.createFunction( { id, name: agent.name }, { event: `${appId}/${id}` }, @@ -56,19 +47,13 @@ export const createServer = ({ } ); } - let networkIdx = 0; + for (const network of networks) { - networkIdx++; - const name = network.name ?? `My network #${networkIdx}`; - if (!network.name) { - console.warn( - `Network missing 'name' option. Created generic name: ${name}` - ); - } - const slug = slugify(name); + const slug = slugify(network.name); const id = `network-${slug}`; + functions[id] = inngest.createFunction( - { id, name }, + { id, name: network.name }, { event: `${appId}/${id}` }, async ({ event }) => { // eslint-disable-next-line From a0b176c8593042f25da6637bf160b4c6558670ca Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:14:32 +0000 Subject: [PATCH 7/7] Fix build error; provide all `Network` opts --- src/networkRun.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/networkRun.ts b/src/networkRun.ts index 706150f..a89ad78 100644 --- a/src/networkRun.ts +++ b/src/networkRun.ts @@ -7,6 +7,8 @@ export class NetworkRun extends Network { constructor(network: Network, state: State) { super({ + name: network.name, + description: network.description, agents: Array.from(network.agents.values()), defaultModel: network.defaultModel, defaultState: network.defaultState, @@ -43,7 +45,7 @@ export class NetworkRun extends Network { // off of the network. const next = await this.getNextAgents( input, - overrides?.router || this.defaultRouter, + overrides?.router || this.defaultRouter ); if (!next) { // TODO: If call count is 0, error. @@ -92,7 +94,7 @@ export class NetworkRun extends Network { // custom code. const next = await this.getNextAgents( input, - overrides?.router || this.defaultRouter, + overrides?.router || this.defaultRouter ); for (const a of next || []) { this.schedule(a.name); @@ -104,7 +106,7 @@ export class NetworkRun extends Network { private async getNextAgents( input: string, - router?: Network.Router, + router?: Network.Router ): Promise { // A router may do one of two things: // @@ -114,7 +116,7 @@ export class NetworkRun extends Network { // It can do this by using code, or by calling routing agents directly. if (!router && !this.defaultModel) { throw new Error( - "No router or model defined in network. You must pass a router or a default model to use the built-in agentic router.", + "No router or model defined in network. You must pass a router or a default model to use the built-in agentic router." ); } if (!router) { @@ -162,7 +164,7 @@ export class NetworkRun extends Network { private async getNextAgentsViaRoutingAgent( routingAgent: RoutingAgent, - input: string, + input: string ): Promise { const result = await routingAgent.run(input, { network: this,