From 0ff0cb5319796c785aed7bb3bcecaa75c61ea01e Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:35:19 +0000 Subject: [PATCH 1/8] Use `@inngest/ai` and optional step tooling --- package.json | 3 ++- pnpm-lock.yaml | 44 ++++++++++++++++++++++++++++-------- src/adapters/anthropic.ts | 14 ++++++------ src/adapters/index.ts | 2 +- src/adapters/openai.ts | 10 ++++----- src/agent.ts | 26 +++++++++++----------- src/model.ts | 47 +++++++++++++++++++++++++++++++-------- src/models.ts | 2 +- src/network.ts | 2 +- src/state.ts | 2 +- src/types.ts | 21 +---------------- src/util.ts | 14 +++++------- 12 files changed, 111 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index a573806..8533b5e 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,9 @@ } }, "dependencies": { + "@inngest/ai": "^0.0.0", "express": "^4.21.1", - "inngest": "^3.29.0", + "inngest": "3.29.4-pr-802.0", "openai-zod-to-json-schema": "^1.0.3", "zod": "^3.23.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb641c3..6a2f81d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,15 @@ importers: .: dependencies: + '@inngest/ai': + specifier: ^0.0.0 + version: 0.0.0 express: specifier: ^4.21.1 version: 4.21.1 inngest: - specifier: ^3.29.0 - version: 3.29.0(express@4.21.1)(typescript@5.7.2) + specifier: 3.29.4-pr-802.0 + version: 3.29.4-pr-802.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) @@ -189,6 +192,9 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} + '@inngest/ai@0.0.0': + resolution: {integrity: sha512-Zic5ECvciYFgLyreuAwlD09/QrlHdCLBBZDodf+h9mTicXB1eq6sWgBOMQ4XiR31qVKVj19ADU7mtqF1+f7Lbg==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -269,6 +275,9 @@ packages: '@types/node@22.10.1': resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/node@22.9.1': resolution: {integrity: sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==} @@ -517,8 +526,8 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -866,8 +875,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inngest@3.29.0: - resolution: {integrity: sha512-TQV9ce8nxNvj1nzOzWb197UHXHuXG3YHAkoSe0U5oqTAE7vs0a4HxQAzTivovsS9Z32f6FVx+m78Fov8sV0lQA==} + inngest@3.29.4-pr-802.0: + resolution: {integrity: sha512-l8QBwiXaOLLodztC3CpAftuOeP7HOYqD+dFWoQeRqlDI+l+nejZ4YLAkzO6abUtGhnIyIkLQN/HMk7XWuVjPQw==} engines: {node: '>=14'} peerDependencies: '@sveltejs/kit': '>=1.27.3' @@ -1469,6 +1478,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -1742,6 +1756,11 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} + '@inngest/ai@0.0.0': + dependencies: + '@types/node': 22.10.5 + typescript: 5.7.3 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} @@ -1832,6 +1851,10 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/node@22.10.5': + dependencies: + undici-types: 6.20.0 + '@types/node@22.9.1': dependencies: undici-types: 6.19.8 @@ -2098,7 +2121,7 @@ snapshots: create-require@1.1.1: {} - cross-fetch@4.0.0: + cross-fetch@4.1.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: @@ -2477,12 +2500,13 @@ snapshots: inherits@2.0.4: {} - inngest@3.29.0(express@4.21.1)(typescript@5.7.2): + inngest@3.29.4-pr-802.0(express@4.21.1)(typescript@5.7.2): dependencies: + '@inngest/ai': 0.0.0 '@types/debug': 4.1.12 canonicalize: 1.0.8 chalk: 4.1.2 - cross-fetch: 4.0.0 + cross-fetch: 4.1.0 debug: 4.3.7(supports-color@5.5.0) hash.js: 1.1.7 json-stringify-safe: 5.0.1 @@ -3007,6 +3031,8 @@ snapshots: typescript@5.7.2: {} + typescript@5.7.3: {} + undefsafe@2.0.5: {} undici-types@6.19.8: {} diff --git a/src/adapters/anthropic.ts b/src/adapters/anthropic.ts index ef6c75d..276cfd9 100644 --- a/src/adapters/anthropic.ts +++ b/src/adapters/anthropic.ts @@ -7,7 +7,7 @@ import { type AiAdapter, type Anthropic, type AnthropicAiAdapter, -} from "inngest"; +} from "@inngest/ai"; import { zodToJsonSchema } from "openai-zod-to-json-schema"; import { z } from "zod"; import { type AgenticModel } from "../model"; @@ -21,12 +21,12 @@ export const requestParser: AgenticModel.RequestParser = ( model, messages, tools, - tool_choice = "auto", + tool_choice = "auto" ) => { // Note that Anthropic has a top-level system prompt, then a series of prompts // for assistants and users. const systemMessage = messages.find( - (m) => m.role === "system" && m.type === "text", + (m) => m.role === "system" && m.type === "text" ) as TextMessage; const system = typeof systemMessage?.content === "string" ? systemMessage.content : ""; @@ -79,7 +79,7 @@ export const requestParser: AgenticModel.RequestParser = ( ]; } }, - [] as AiAdapter.Input["messages"], + [] as AiAdapter.Input["messages"] ); const request: AiAdapter.Input = { @@ -97,7 +97,7 @@ export const requestParser: AgenticModel.RequestParser = ( input_schema: (t.parameters ? zodToJsonSchema(t.parameters) : zodToJsonSchema( - z.object({}), + z.object({}) )) as AnthropicAiAdapter.Tool.InputSchema, }; }); @@ -111,7 +111,7 @@ export const requestParser: AgenticModel.RequestParser = ( * Parse a response from Anthropic output to internal network messages. */ export const responseParser: AgenticModel.ResponseParser = ( - input, + input ) => { return (input?.content ?? []).reduce((acc, item) => { if (!item.type) { @@ -165,7 +165,7 @@ export const responseParser: AgenticModel.ResponseParser = ( }; const toolChoice = ( - choice: Tool.Choice, + choice: Tool.Choice ): AiAdapter.Input["tool_choice"] => { switch (choice) { case "auto": diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 99e8396..5942033 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -1,4 +1,4 @@ -import { type AiAdapter, type AiAdapters } from "inngest"; +import { type AiAdapter, type AiAdapters } from "@inngest/ai"; import { type AgenticModel } from "../model"; import * as anthropic from "./anthropic"; import * as openai from "./openai"; diff --git a/src/adapters/openai.ts b/src/adapters/openai.ts index f0f6139..3a2479d 100644 --- a/src/adapters/openai.ts +++ b/src/adapters/openai.ts @@ -4,7 +4,7 @@ * @module */ -import { type AiAdapter, type OpenAi } from "inngest"; +import { type AiAdapter, type OpenAi } from "@inngest/ai"; import { zodToJsonSchema } from "openai-zod-to-json-schema"; import { type AgenticModel } from "../model"; import { @@ -23,7 +23,7 @@ export const requestParser: AgenticModel.RequestParser = ( model, messages, tools, - tool_choice = "auto", + tool_choice = "auto" ) => { const request: AiAdapter.Input = { messages: messages.map((m) => { @@ -82,7 +82,7 @@ export const requestParser: AgenticModel.RequestParser = ( * Parse a response from OpenAI output to internal network messages. */ export const responseParser: AgenticModel.ResponseParser = ( - input, + input ) => { return (input?.choices ?? []).reduce((acc, choice) => { const { message, finish_reason } = choice; @@ -149,12 +149,12 @@ const safeParseOpenAIJson = (str: string): unknown => { // Replace backtick strings with regular JSON strings // Match content between backticks, preserving newlines const withQuotes = trimmed.replace(/`([\s\S]*?)`/g, (_, content) => - JSON.stringify(content), + JSON.stringify(content) ); return JSON.parse(withQuotes); } catch (e) { throw new Error( - `Failed to parse JSON with backticks: ${stringifyError(e)}`, + `Failed to parse JSON with backticks: ${stringifyError(e)}` ); } } diff --git a/src/agent.ts b/src/agent.ts index f8831f4..efa48cb 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -1,4 +1,4 @@ -import { type AiAdapter } from "inngest"; +import { type AiAdapter } from "@inngest/ai"; import { createAgenticModelFromAiAdapter, type AgenticModel } from "./model"; import { NetworkRun } from "./networkRun"; import { @@ -8,7 +8,7 @@ import { type ToolResultMessage, } from "./state"; import { type Tool } from "./types"; -import { getStepTools, type AnyZodType, type MaybePromise } from "./util"; +import { type AnyZodType, type MaybePromise } from "./util"; /** * createTool is a helper that properly types the input argument for a handler @@ -108,7 +108,7 @@ export class Agent { */ async run( input: string, - { model, network, state, maxIter = 0 }: Agent.RunOptions | undefined = {}, + { model, network, state, maxIter = 0 }: Agent.RunOptions | undefined = {} ): Promise { const rawModel = model || this.model || network?.defaultModel; if (!rawModel) { @@ -152,7 +152,7 @@ export class Agent { p, prompt, history, - run, + run ); hasMoreActions = @@ -183,13 +183,13 @@ export class Agent { p: AgenticModel.Any, prompt: Message[], history: Message[], - network?: NetworkRun, + network?: NetworkRun ): Promise { const { output, raw } = await p.infer( this.name, prompt.concat(history), Array.from(this.tools.values()), - this.tool_choice || "auto", + this.tool_choice || "auto" ); // Now that we've made the call, we instantiate a new InferenceResult for @@ -201,7 +201,7 @@ export class Agent { history, output, [], - typeof raw === "string" ? raw : JSON.stringify(raw), + typeof raw === "string" ? raw : JSON.stringify(raw) ); if (this.lifecycles?.onResponse) { result = await this.lifecycles.onResponse({ @@ -223,7 +223,7 @@ export class Agent { private async invokeTools( msgs: Message[], p: AgenticModel.Any, - network?: NetworkRun, + network?: NetworkRun ): Promise { const output: ToolResultMessage[] = []; @@ -240,7 +240,7 @@ export class Agent { const found = this.tools.get(tool.name); if (!found) { throw new Error( - `Inference requested a non-existent tool: ${tool.name}`, + `Inference requested a non-existent tool: ${tool.name}` ); } @@ -254,7 +254,7 @@ export class Agent { const result = await found.handler(tool.input, { agent: this, network, - step: await getStepTools(), + // step: await getStepTools(), }); // TODO: handle error and send them back to the LLM @@ -280,7 +280,7 @@ export class Agent { private async agentPrompt( input: string, - network?: NetworkRun, + network?: NetworkRun ): Promise { // Prompt returns the full prompt for the current agent. This does NOT // include the existing network's state as part of the prompt. @@ -401,7 +401,7 @@ export namespace Agent { * running tools. */ onResponse?: ( - args: Agent.LifecycleArgs.Result, + args: Agent.LifecycleArgs.Result ) => MaybePromise; /** @@ -411,7 +411,7 @@ export namespace Agent { * */ onFinish?: ( - args: Agent.LifecycleArgs.Result, + args: Agent.LifecycleArgs.Result ) => MaybePromise; } diff --git a/src/model.ts b/src/model.ts index 28d9024..faf2c8a 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,4 +1,4 @@ -import { type AiAdapter } from "inngest"; +import { type AiAdapter } from "@inngest/ai"; import { adapters } from "./adapters"; import { type Message } from "./state"; import { type Tool } from "./types"; @@ -7,7 +7,7 @@ import { getStepTools } from "./util"; export const createAgenticModelFromAiAdapter = < TAiAdapter extends AiAdapter.Any, >( - adapter: TAiAdapter, + adapter: TAiAdapter ): AgenticModel => { const opts = adapters[adapter.format as AiAdapter.Format]; @@ -39,14 +39,43 @@ export class AgenticModel { stepID: string, input: Message[], tools: Tool.Any[], - tool_choice: Tool.Choice, + tool_choice: Tool.Choice ): Promise { + const body = this.requestParser(this.#model, input, tools, tool_choice); + let result: AiAdapter.Input; + const step = await getStepTools(); - const result = (await step.ai.infer(stepID, { - model: this.#model, - body: this.requestParser(this.#model, input, tools, tool_choice), - })) as AiAdapter.Input; + if (step) { + result = (await step.ai.infer(stepID, { + model: this.#model, + body, + })) as AiAdapter.Input; + } else { + const url = new URL(this.#model.url || ""); + + const headers: Record = { + "Content-Type": "application/json", + }; + + // Make sure we handle every known format in `@inngest/ai`. + const formatHandlers: Record void> = { + "openai-chat": () => { + headers["Authorization"] = `Bearer ${this.#model.authKey}`; + }, + anthropic: () => { + headers["x-api-key"] = this.#model.authKey; + }, + }; + + formatHandlers[this.#model.format as AiAdapter.Format](); + + result = await fetch(url, { + method: "POST", + headers, + body, + }); + } return { output: this.responseParser(result), raw: result }; } @@ -75,10 +104,10 @@ export namespace AgenticModel { model: TAiAdapter, state: Message[], tools: Tool.Any[], - tool_choice: Tool.Choice, + tool_choice: Tool.Choice ) => AiAdapter.Input; export type ResponseParser = ( - output: AiAdapter.Output, + output: AiAdapter.Output ) => Message[]; } diff --git a/src/models.ts b/src/models.ts index dd85ef4..d90ff01 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1 +1 @@ -export { openai, anthropic, gemini } from "inngest"; +export { anthropic, gemini, openai } from "@inngest/ai"; diff --git a/src/network.ts b/src/network.ts index 6087ad1..8a64d28 100644 --- a/src/network.ts +++ b/src/network.ts @@ -1,4 +1,4 @@ -import { type AiAdapter } from "inngest"; +import { type AiAdapter } from "@inngest/ai"; import { z } from "zod"; import { createRoutingAgent, diff --git a/src/state.ts b/src/state.ts index daebc45..2d0f4b0 100644 --- a/src/state.ts +++ b/src/state.ts @@ -174,7 +174,7 @@ export class InferenceResult { // raw represents the raw API response from the call. This is a JSON // string, and the format depends on the agent's model. - public raw: string, + public raw: string ) {} withFormatter(f: (a: InferenceResult) => Message[]) { diff --git a/src/types.ts b/src/types.ts index 6b4cc5b..96afb76 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,7 @@ -import { type GetStepTools, type Inngest } from "inngest"; import { type output as ZodOutput } from "zod"; import { type Agent } from "./agent"; import { type NetworkRun } from "./networkRun"; -import { - type AnyZodType, - type GenericizeFunctionsInObject, - type MaybePromise, - type SimplifyDeep, -} from "./util"; +import { type AnyZodType, type MaybePromise } from "./util"; export type Tool = { name: string; @@ -32,17 +26,4 @@ export namespace Tool { export type ToolHandlerArgs = { agent: Agent; network?: NetworkRun; - step: GetStepTools; }; - -/** - * Represents step tooling from an Inngest client, purposefully genericized to - * allow for more flexible usage. - * - * Prefer use of `GetStepTools` in most cases, especially when you have access - * to a client. - */ -export type AnyStepTools = SimplifyDeep< - GenericizeFunctionsInObject> -> & - Record; diff --git a/src/util.ts b/src/util.ts index 6a0816f..7292f34 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,4 @@ -import { getAsyncCtx } from "inngest/experimental"; +import { type AsyncContext, getAsyncCtx } from "inngest/experimental"; import { type ZodType } from "zod"; export type MaybePromise = T | Promise; @@ -29,16 +29,14 @@ export const stringifyError = (e: unknown): string => { }; /** - * Attempts to retrieve the step tools from the async context. If the context is - * not found, an error is thrown. + * Attempts to retrieve the step tools from the async context. */ -export const getStepTools = async () => { +export const getStepTools = async (): Promise< + AsyncContext["ctx"]["step"] | undefined +> => { const asyncCtx = await getAsyncCtx(); - if (!asyncCtx) { - throw new Error("Could not find Inngest step tooling in async context"); - } - return asyncCtx.ctx.step; + return asyncCtx?.ctx.step; }; /** From 9f83f9718cdfa5ba926b91b70c4af063d8008b69 Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:26:28 +0000 Subject: [PATCH 2/8] Make sure to get JSON response of `infer()` call --- src/model.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/model.ts b/src/model.ts index faf2c8a..aeed52f 100644 --- a/src/model.ts +++ b/src/model.ts @@ -70,11 +70,13 @@ export class AgenticModel { formatHandlers[this.#model.format as AiAdapter.Format](); - result = await fetch(url, { - method: "POST", - headers, - body, - }); + result = ( + await fetch(url, { + method: "POST", + headers, + body, + }) + ).json(); } return { output: this.responseParser(result), raw: result }; From 705390a3daf75c6db857b44ed3cc50d3cc53325f Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:20:43 +0000 Subject: [PATCH 3/8] Use set `@inngest/ai` versions `types` type needs loosening. --- package.json | 4 ++-- pnpm-lock.yaml | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 1c040d9..e6a9bb8 100644 --- a/package.json +++ b/package.json @@ -46,11 +46,11 @@ }, "dependencies": { "@dmitryrechkin/json-schema-to-zod": "^1.0.0", - "@inngest/ai": "^0.0.2", + "@inngest/ai": "^0.0.0", "@modelcontextprotocol/sdk": "^1.1.1", "eventsource": "^3.0.2", "express": "^4.21.1", - "inngest": "^3.29.0", + "inngest": "^3.30.0", "openai-zod-to-json-schema": "^1.0.3", "zod": "^3.23.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc25d9f..d781035 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 '@inngest/ai': - specifier: ^0.0.2 - version: 0.0.2 + specifier: ^0.0.0 + version: 0.0.0 '@modelcontextprotocol/sdk': specifier: ^1.1.1 version: 1.1.1 @@ -24,8 +24,8 @@ importers: specifier: ^4.21.1 version: 4.21.1 inngest: - specifier: ^3.29.0 - version: 3.29.0(express@4.21.1)(typescript@5.7.2) + specifier: ^3.30.0 + version: 3.30.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) @@ -345,8 +345,8 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} - '@inngest/ai@0.0.2': - resolution: {integrity: sha512-PEU0gXvAGFDU3F6Z0PpmLSsi22qv5nbOmnOOueGZUYyXNXIm7tNZ/QryreeqdqhfU/LWYzmnJN1APycdoBwuBg==} + '@inngest/ai@0.0.0': + resolution: {integrity: sha512-Zic5ECvciYFgLyreuAwlD09/QrlHdCLBBZDodf+h9mTicXB1eq6sWgBOMQ4XiR31qVKVj19ADU7mtqF1+f7Lbg==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} @@ -823,8 +823,8 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -1203,8 +1203,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inngest@3.29.0: - resolution: {integrity: sha512-TQV9ce8nxNvj1nzOzWb197UHXHuXG3YHAkoSe0U5oqTAE7vs0a4HxQAzTivovsS9Z32f6FVx+m78Fov8sV0lQA==} + inngest@3.30.0: + resolution: {integrity: sha512-MLl66ylItUVqAxVr9dqYPaynh45DKBZo9TV+hfWk5x4A8vFC52aIhTM5EsCp4o2kTfZSYjNnalR7ROMbK56plA==} engines: {node: '>=14'} peerDependencies: '@sveltejs/kit': '>=1.27.3' @@ -2285,7 +2285,7 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@inngest/ai@0.0.2': + '@inngest/ai@0.0.0': dependencies: '@types/node': 22.10.7 typescript: 5.7.3 @@ -2767,7 +2767,7 @@ snapshots: create-require@1.1.1: {} - cross-fetch@4.0.0: + cross-fetch@4.1.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: @@ -3192,12 +3192,13 @@ snapshots: inherits@2.0.4: {} - inngest@3.29.0(express@4.21.1)(typescript@5.7.2): + inngest@3.30.0(express@4.21.1)(typescript@5.7.2): dependencies: + '@inngest/ai': 0.0.0 '@types/debug': 4.1.12 canonicalize: 1.0.8 chalk: 4.1.2 - cross-fetch: 4.0.0 + cross-fetch: 4.1.0 debug: 4.3.7(supports-color@5.5.0) hash.js: 1.1.7 json-stringify-safe: 5.0.1 From f327c7cd19ad0b11bf97ef407b2d80e92b1e9794 Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:43:31 +0000 Subject: [PATCH 4/8] Await `fetch().json()` --- src/model.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model.ts b/src/model.ts index aeed52f..20952cd 100644 --- a/src/model.ts +++ b/src/model.ts @@ -70,7 +70,8 @@ export class AgenticModel { formatHandlers[this.#model.format as AiAdapter.Format](); - result = ( + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + result = await ( await fetch(url, { method: "POST", headers, From 67e8a98a9c1b7348e5d2031fbad7a6fd9486f5ee Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:49:54 +0000 Subject: [PATCH 5/8] Make sure we call `Model#onCall()` if we're not using `step.ai.infer()` --- src/model.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/model.ts b/src/model.ts index 20952cd..49a676e 100644 --- a/src/model.ts +++ b/src/model.ts @@ -52,7 +52,11 @@ export class AgenticModel { body, })) as AiAdapter.Input; } else { - const url = new URL(this.#model.url || ""); + // Allow the model to mutate options and body for this call + const modelCopy = { ...this.#model }; + this.#model.onCall?.(modelCopy, body); + + const url = new URL(modelCopy.url || ""); const headers: Record = { "Content-Type": "application/json", @@ -61,14 +65,14 @@ export class AgenticModel { // Make sure we handle every known format in `@inngest/ai`. const formatHandlers: Record void> = { "openai-chat": () => { - headers["Authorization"] = `Bearer ${this.#model.authKey}`; + headers["Authorization"] = `Bearer ${modelCopy.authKey}`; }, anthropic: () => { - headers["x-api-key"] = this.#model.authKey; + headers["x-api-key"] = modelCopy.authKey; }, }; - formatHandlers[this.#model.format as AiAdapter.Format](); + formatHandlers[modelCopy.format as AiAdapter.Format](); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment result = await ( From a26046d37b8030f25edeb512429b5728b61b2ceb Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:57:47 +0000 Subject: [PATCH 6/8] Remove comment --- packages/agent-kit/src/agent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/agent-kit/src/agent.ts b/packages/agent-kit/src/agent.ts index 70575b5..74c085e 100644 --- a/packages/agent-kit/src/agent.ts +++ b/packages/agent-kit/src/agent.ts @@ -282,7 +282,6 @@ export class Agent { const result = await found.handler(tool.input, { agent: this, network, - // step: await getStepTools(), }); // TODO: handle error and send them back to the LLM From 0baede6415694049d2c0519c0741eb1f12bd4174 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Mon, 20 Jan 2025 15:58:52 +0000 Subject: [PATCH 7/8] Create seven-beers-press.md --- .changeset/seven-beers-press.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-beers-press.md diff --git a/.changeset/seven-beers-press.md b/.changeset/seven-beers-press.md new file mode 100644 index 0000000..b8fd2c0 --- /dev/null +++ b/.changeset/seven-beers-press.md @@ -0,0 +1,5 @@ +--- +"@inngest/agent-kit": patch +--- + +Use `@inngest/ai` and only optionally use step tooling From 9f620ea9a256e1a63971dd9e0882b1a263df5a7d Mon Sep 17 00:00:00 2001 From: Jack Williams <1736957+jpwilliams@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:07:43 +0000 Subject: [PATCH 8/8] Provide optional access to step tooling in agent tool handlers --- examples/swebench/agents/planner.ts | 2 +- packages/agent-kit/src/agent.ts | 1 + packages/agent-kit/src/types.ts | 3 ++- packages/agent-kit/src/util.ts | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/swebench/agents/planner.ts b/examples/swebench/agents/planner.ts index 4679c0d..e47bb3b 100644 --- a/examples/swebench/agents/planner.ts +++ b/examples/swebench/agents/planner.ts @@ -32,7 +32,7 @@ export const planningAgent = createAgent({ handler: async (plan, opts) => { // Store this in the function state for introspection in tracing. - await opts.step.run("plan created", () => plan); + await opts.step?.run("plan created", () => plan); opts.network?.state.kv.set("plan", plan); }, }), diff --git a/packages/agent-kit/src/agent.ts b/packages/agent-kit/src/agent.ts index 74c085e..2d09576 100644 --- a/packages/agent-kit/src/agent.ts +++ b/packages/agent-kit/src/agent.ts @@ -282,6 +282,7 @@ export class Agent { const result = await found.handler(tool.input, { agent: this, network, + step: await getStepTools(), }); // TODO: handle error and send them back to the LLM diff --git a/packages/agent-kit/src/types.ts b/packages/agent-kit/src/types.ts index 593cd82..da9a7f8 100644 --- a/packages/agent-kit/src/types.ts +++ b/packages/agent-kit/src/types.ts @@ -1,7 +1,7 @@ import { type output as ZodOutput } from "zod"; import { type Agent } from "./agent"; import { type NetworkRun } from "./networkRun"; -import { type AnyZodType, type MaybePromise } from "./util"; +import { type AnyZodType, type MaybePromise, type StepTools } from "./util"; export type Tool = { name: string; @@ -63,4 +63,5 @@ export namespace MCP { export type ToolHandlerArgs = { agent: Agent; network?: NetworkRun; + step?: StepTools; }; diff --git a/packages/agent-kit/src/util.ts b/packages/agent-kit/src/util.ts index 7292f34..a80b6bc 100644 --- a/packages/agent-kit/src/util.ts +++ b/packages/agent-kit/src/util.ts @@ -39,6 +39,8 @@ export const getStepTools = async (): Promise< return asyncCtx?.ctx.step; }; +export type StepTools = Awaited>; + /** * Given an object `T`, return a new object where all keys with function types * as values are genericized. If the value is an object, recursively apply this