@aigne/core 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import type { Context, ContextState } from "./context";
2
2
  import type { MemoryItemWithScore, MemoryMessage } from "./memorable";
3
3
  import { type RunOptions, Runnable, type RunnableResponse, type RunnableResponseChunk, type RunnableResponseStream } from "./runnable";
4
+ import type { BindAgentInputs, BoundAgent } from "./utils/runnable-type";
4
5
  export interface AgentProcessOptions<Memories extends {
5
6
  [name: string]: MemoryItemWithScore[];
6
7
  }> {
@@ -38,4 +39,13 @@ export declare abstract class Agent<I extends {
38
39
  */
39
40
  protected onResult(_result: O): Promise<void>;
40
41
  abstract process(input: I, options: AgentProcessOptions<Memories>): Promise<RunnableResponse<O> | AsyncGenerator<RunnableResponseChunk<O>, void>> | AsyncGenerator<RunnableResponseChunk<O>, void>;
42
+ /**
43
+ * Bind some inputs to the agent, used for process of `PipelineAgent` or case of `LLMDecisionAgent`.
44
+ * @param options The bind options.
45
+ * @returns The bound agent.
46
+ */
47
+ bind<Input extends BindAgentInputs<typeof this>>(options: {
48
+ description?: string;
49
+ input?: Input;
50
+ }): BoundAgent<typeof this, Readonly<Input>>;
41
51
  }
package/lib/cjs/agent.js CHANGED
@@ -100,5 +100,16 @@ class Agent extends runnable_1.Runnable {
100
100
  async onResult(_result) {
101
101
  // Override this method to perform additional operations before the result is returned
102
102
  }
103
+ /**
104
+ * Bind some inputs to the agent, used for process of `PipelineAgent` or case of `LLMDecisionAgent`.
105
+ * @param options The bind options.
106
+ * @returns The bound agent.
107
+ */
108
+ bind(options) {
109
+ return {
110
+ ...options,
111
+ runnable: this,
112
+ };
113
+ }
103
114
  }
104
115
  exports.Agent = Agent;
@@ -1,14 +1,12 @@
1
1
  import { Agent, type AgentProcessOptions } from "./agent";
2
2
  import type { Context, ContextState } from "./context";
3
- import type { DataTypeSchema } from "./definitions/data-type-schema";
4
3
  import { type CreateRunnableMemory } from "./definitions/memory";
5
4
  import type { CreateLLMAgentOptions, LLMAgentDefinition } from "./llm-agent";
6
5
  import type { LLMModel } from "./llm-model";
7
6
  import type { MemorableSearchOutput, MemoryItemWithScore } from "./memorable";
8
- import type { Runnable, RunnableDefinition } from "./runnable";
7
+ import type { RunnableDefinition } from "./runnable";
9
8
  import { OrderedRecord } from "./utils";
10
- import type { ExtractRunnableInputType, ExtractRunnableOutputType } from "./utils/runnable-type";
11
- import type { UnionToIntersection } from "./utils/union";
9
+ import type { BindAgentInput, BoundAgent, ExtractRunnableInputTypeIntersection, ExtractRunnableOutputType, OmitBoundAgentInput } from "./utils/runnable-type";
12
10
  export declare class LLMDecisionAgent<I extends {
13
11
  [name: string]: any;
14
12
  } = {}, O extends {
@@ -22,30 +20,7 @@ export declare class LLMDecisionAgent<I extends {
22
20
  constructor(definition: LLMDecisionAgentDefinition, context?: Context<State>, model?: LLMModel | undefined);
23
21
  process(input: I, options: AgentProcessOptions<Memories>): Promise<import("./runnable").RunnableResponse<O>>;
24
22
  }
25
- export interface DecisionAgentCaseParameter<R extends Runnable = Runnable> {
26
- description?: string;
27
- runnable: R;
28
- }
29
- /**
30
- * Options to create LLMDecisionAgent.
31
- */
32
- export interface CreateLLMDecisionAgentOptions<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
33
- [name: string]: DataTypeSchema;
34
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
35
- [name: string]: DataTypeSchema;
36
- }>, Memories extends {
37
- [name: string]: CreateRunnableMemory<I>;
38
- }, State extends ContextState> extends Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> {
39
- context: Context<State>;
40
- cases: {
41
- [name: string]: Case;
42
- };
43
- }
44
- declare function create<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
45
- [name: string]: DataTypeSchema;
46
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
47
- [name: string]: DataTypeSchema;
48
- }>, Memories extends {
23
+ declare function create<Case extends BoundAgent, I extends ExtractRunnableInputTypeIntersection<Case["runnable"]>, O extends ExtractRunnableOutputType<Case["runnable"]>, Memories extends {
49
24
  [name: string]: CreateRunnableMemory<I> & {
50
25
  /**
51
26
  * Whether this memory is primary? Primary memory will be passed as messages to LLM chat model,
@@ -55,7 +30,12 @@ declare function create<Case extends DecisionAgentCaseParameter, I extends Union
55
30
  */
56
31
  primary?: boolean;
57
32
  };
58
- }, State extends ContextState>({ context, ...options }: CreateLLMDecisionAgentOptions<Case, I, O, Memories, State>): LLMDecisionAgent<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, ExtractRunnableOutputType<Case["runnable"]>, {
33
+ }, State extends ContextState>({ context, ...options }: Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> & {
34
+ context: Context<State>;
35
+ cases: {
36
+ [name: string]: Case;
37
+ };
38
+ }): LLMDecisionAgent<OmitBoundAgentInput<Case, "ai">, ExtractRunnableOutputType<Case["runnable"]>, {
59
39
  [name in keyof Memories]: MemorableSearchOutput<Memories[name]["memory"]>;
60
40
  }, State>;
61
41
  export interface LLMDecisionAgentDefinition extends RunnableDefinition, Pick<LLMAgentDefinition, "modelOptions" | "messages" | "primaryMemoryId"> {
@@ -69,5 +49,8 @@ export interface LLMDecisionCase {
69
49
  runnable?: {
70
50
  id?: string;
71
51
  };
52
+ input?: {
53
+ [inputId: string]: BindAgentInput;
54
+ };
72
55
  }
73
56
  export {};
@@ -20,6 +20,7 @@ const constants_1 = require("./constants");
20
20
  const memory_1 = require("./definitions/memory");
21
21
  const utils_1 = require("./utils");
22
22
  const message_utils_1 = require("./utils/message-utils");
23
+ const structured_output_schema_1 = require("./utils/structured-output-schema");
23
24
  let LLMDecisionAgent = class LLMDecisionAgent extends agent_1.Agent {
24
25
  definition;
25
26
  model;
@@ -45,19 +46,23 @@ let LLMDecisionAgent = class LLMDecisionAgent extends agent_1.Agent {
45
46
  const name = t.name || runnable.name;
46
47
  if (!name)
47
48
  throw new Error("Case name is required");
48
- return { name, description: t.description, runnable };
49
+ return { ...t, name, runnable };
49
50
  }));
50
51
  const llmInputs = {
51
52
  messages: messagesWithMemory,
52
53
  modelOptions: definition.modelOptions,
53
54
  tools: cases.map((t) => {
54
- // TODO: auto generate parameters by llm model if needed
55
+ // Filter inputs that are bound from AI
56
+ const inputsFromAI = utils_1.OrderedRecord.fromArray(utils_1.OrderedRecord.filter(t.runnable.definition.inputs, (i) => t.input?.[i.id]?.from === "ai"));
57
+ const parameters = inputsFromAI.$indexes.length > 0
58
+ ? (0, structured_output_schema_1.outputsToJsonSchema)(inputsFromAI)
59
+ : {};
55
60
  return {
56
61
  type: "function",
57
62
  function: {
58
63
  name: t.name,
59
64
  description: t.description,
60
- parameters: {},
65
+ parameters,
61
66
  },
62
67
  };
63
68
  }),
@@ -65,14 +70,16 @@ let LLMDecisionAgent = class LLMDecisionAgent extends agent_1.Agent {
65
70
  };
66
71
  const { toolCalls } = await model.run(llmInputs);
67
72
  // TODO: support run multiple calls
68
- const functionNameToCall = toolCalls?.[0]?.function?.name;
73
+ const { name: functionNameToCall, arguments: args } = toolCalls?.[0]?.function ?? {};
69
74
  if (!functionNameToCall)
70
75
  throw new Error("No any runnable called");
71
76
  const caseToCall = cases.find((i) => i.name === functionNameToCall);
72
77
  if (!caseToCall)
73
78
  throw new Error("Case not found");
79
+ // Prepare arguments generated by LLM model
80
+ const llmArgs = args ? JSON.parse(args) : {};
74
81
  // TODO: check result structure and omit undefined values
75
- const output = await caseToCall.runnable.run(input, { stream: true });
82
+ const output = await caseToCall.runnable.run({ ...input, ...llmArgs }, { stream: true });
76
83
  return (0, utils_1.extractOutputsFromRunnableOutput)(output, ({ $text, ...json }) => this.updateMemories([
77
84
  ...originalMessages,
78
85
  {
@@ -92,12 +99,22 @@ exports.LLMDecisionAgent = LLMDecisionAgent = __decorate([
92
99
  ], LLMDecisionAgent);
93
100
  function create({ context, ...options }) {
94
101
  const agentId = options.name || (0, nanoid_1.nanoid)();
95
- const cases = utils_1.OrderedRecord.fromArray(Object.entries(options.cases).map(([name, c]) => ({
96
- id: (0, nanoid_1.nanoid)(),
97
- name: name || c.runnable.name,
98
- description: c.description,
99
- runnable: { id: c.runnable.id },
100
- })));
102
+ const cases = utils_1.OrderedRecord.fromArray(Object.entries(options.cases).map(([name, c]) => {
103
+ const bindInputs = Object.entries(c.input ?? {});
104
+ return {
105
+ id: (0, nanoid_1.nanoid)(),
106
+ name: name || c.runnable.name,
107
+ description: c.description,
108
+ runnable: { id: c.runnable.id },
109
+ input: Object.fromEntries(bindInputs.map(([inputName, v]) => {
110
+ const input = c.runnable.definition.inputs[inputName] ||
111
+ utils_1.OrderedRecord.find(c.runnable.definition.inputs, (i) => i.name === inputName);
112
+ if (!input)
113
+ throw new Error(`Input ${inputName} not found`);
114
+ return [input.id, v];
115
+ })),
116
+ };
117
+ }));
101
118
  const inputs = utils_1.OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.inputs));
102
119
  const outputs = utils_1.OrderedRecord.fromArray(utils_1.OrderedRecord.map(utils_1.OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.outputs)), (o) => ({ ...o, required: false })));
103
120
  const memories = (0, memory_1.toRunnableMemories)(agentId, inputs, options.memories ?? {});
@@ -8,7 +8,9 @@ export declare class OpenaiLLMModel extends LLMModel {
8
8
  private client;
9
9
  setApiKey(apiKey: string): void;
10
10
  process(input: LLMModelInputs): AsyncGenerator<{
11
- $text: string | undefined;
11
+ $text: string;
12
+ delta?: undefined;
13
+ } | {
12
14
  delta: {
13
15
  toolCalls: {
14
16
  id?: string;
@@ -19,5 +21,6 @@ export declare class OpenaiLLMModel extends LLMModel {
19
21
  };
20
22
  }[];
21
23
  };
24
+ $text?: undefined;
22
25
  }, void, unknown>;
23
26
  }
@@ -43,22 +43,22 @@ class OpenaiLLMModel extends llm_model_1.LLMModel {
43
43
  const toolCalls = [];
44
44
  for await (const chunk of res) {
45
45
  const choice = chunk.choices?.[0];
46
- const calls = choice?.delta.tool_calls?.map((i) => ({
47
- id: i.id || (0, nanoid_1.nanoid)(),
48
- type: "function",
49
- function: {
50
- name: i.function?.name,
51
- arguments: i.function?.arguments,
52
- },
53
- }));
54
- if (calls?.length) {
55
- toolCalls.push(...calls);
46
+ if (choice?.delta.tool_calls?.length) {
47
+ for (const call of choice.delta.tool_calls) {
48
+ const tool = toolCalls[call.index] ?? { id: call.id || (0, nanoid_1.nanoid)() };
49
+ toolCalls[call.index] = tool;
50
+ if (call.type)
51
+ tool.type = call.type;
52
+ tool.function ??= {};
53
+ tool.function.name =
54
+ (tool.function.name || "") + (call.function?.name || "");
55
+ tool.function.arguments = (tool.function.arguments || "").concat(call.function?.arguments || "");
56
+ }
56
57
  }
57
- yield {
58
- $text: choice?.delta.content || undefined,
59
- delta: { toolCalls },
60
- };
58
+ if (choice?.delta.content)
59
+ yield { $text: choice.delta.content };
61
60
  }
61
+ yield { delta: { toolCalls } };
62
62
  }
63
63
  }
64
64
  exports.OpenaiLLMModel = OpenaiLLMModel;
@@ -1,3 +1,23 @@
1
1
  import type { Runnable, RunnableResponseStream } from "../runnable";
2
+ import type { UnionToIntersection } from "./union";
2
3
  export type ExtractRunnableInputType<T> = T extends Runnable<infer I, any> ? I : never;
3
4
  export type ExtractRunnableOutputType<T> = T extends Runnable<any, infer O> ? Exclude<O, RunnableResponseStream<any>> : never;
5
+ export type ExtractRunnableInputTypeIntersection<T> = UnionToIntersection<ExtractRunnableInputType<T>, {}>;
6
+ export type ExtractRunnableOutputTypeIntersection<T> = UnionToIntersection<ExtractRunnableOutputType<T>, {}>;
7
+ export type BindAgentInput = {
8
+ from: "ai";
9
+ };
10
+ export type PickInputFrom<I, From extends BindAgentInput["from"]> = {
11
+ [key in keyof I as I[key] extends {
12
+ from: From;
13
+ } ? key : never]: I[key];
14
+ };
15
+ export type OmitBoundAgentInput<Case extends BoundAgent, From extends BindAgentInput["from"]> = Omit<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, keyof PickInputFrom<Required<UnionToIntersection<NonNullable<Case["input"]>, {}>>, From>>;
16
+ export type BindAgentInputs<R extends Runnable> = {
17
+ [key in keyof ExtractRunnableInputType<R>]?: BindAgentInput;
18
+ };
19
+ export interface BoundAgent<R extends Runnable = Runnable, I extends BindAgentInputs<R> = BindAgentInputs<R>> {
20
+ description?: string;
21
+ runnable: R;
22
+ input?: I;
23
+ }
@@ -1,6 +1,7 @@
1
1
  import type { Context, ContextState } from "./context";
2
2
  import type { MemoryItemWithScore, MemoryMessage } from "./memorable";
3
3
  import { type RunOptions, Runnable, type RunnableResponse, type RunnableResponseChunk, type RunnableResponseStream } from "./runnable";
4
+ import type { BindAgentInputs, BoundAgent } from "./utils/runnable-type";
4
5
  export interface AgentProcessOptions<Memories extends {
5
6
  [name: string]: MemoryItemWithScore[];
6
7
  }> {
@@ -38,4 +39,13 @@ export declare abstract class Agent<I extends {
38
39
  */
39
40
  protected onResult(_result: O): Promise<void>;
40
41
  abstract process(input: I, options: AgentProcessOptions<Memories>): Promise<RunnableResponse<O> | AsyncGenerator<RunnableResponseChunk<O>, void>> | AsyncGenerator<RunnableResponseChunk<O>, void>;
42
+ /**
43
+ * Bind some inputs to the agent, used for process of `PipelineAgent` or case of `LLMDecisionAgent`.
44
+ * @param options The bind options.
45
+ * @returns The bound agent.
46
+ */
47
+ bind<Input extends BindAgentInputs<typeof this>>(options: {
48
+ description?: string;
49
+ input?: Input;
50
+ }): BoundAgent<typeof this, Readonly<Input>>;
41
51
  }
@@ -1,14 +1,12 @@
1
1
  import { Agent, type AgentProcessOptions } from "./agent";
2
2
  import type { Context, ContextState } from "./context";
3
- import type { DataTypeSchema } from "./definitions/data-type-schema";
4
3
  import { type CreateRunnableMemory } from "./definitions/memory";
5
4
  import type { CreateLLMAgentOptions, LLMAgentDefinition } from "./llm-agent";
6
5
  import type { LLMModel } from "./llm-model";
7
6
  import type { MemorableSearchOutput, MemoryItemWithScore } from "./memorable";
8
- import type { Runnable, RunnableDefinition } from "./runnable";
7
+ import type { RunnableDefinition } from "./runnable";
9
8
  import { OrderedRecord } from "./utils";
10
- import type { ExtractRunnableInputType, ExtractRunnableOutputType } from "./utils/runnable-type";
11
- import type { UnionToIntersection } from "./utils/union";
9
+ import type { BindAgentInput, BoundAgent, ExtractRunnableInputTypeIntersection, ExtractRunnableOutputType, OmitBoundAgentInput } from "./utils/runnable-type";
12
10
  export declare class LLMDecisionAgent<I extends {
13
11
  [name: string]: any;
14
12
  } = {}, O extends {
@@ -22,30 +20,7 @@ export declare class LLMDecisionAgent<I extends {
22
20
  constructor(definition: LLMDecisionAgentDefinition, context?: Context<State>, model?: LLMModel | undefined);
23
21
  process(input: I, options: AgentProcessOptions<Memories>): Promise<import("./runnable").RunnableResponse<O>>;
24
22
  }
25
- export interface DecisionAgentCaseParameter<R extends Runnable = Runnable> {
26
- description?: string;
27
- runnable: R;
28
- }
29
- /**
30
- * Options to create LLMDecisionAgent.
31
- */
32
- export interface CreateLLMDecisionAgentOptions<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
33
- [name: string]: DataTypeSchema;
34
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
35
- [name: string]: DataTypeSchema;
36
- }>, Memories extends {
37
- [name: string]: CreateRunnableMemory<I>;
38
- }, State extends ContextState> extends Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> {
39
- context: Context<State>;
40
- cases: {
41
- [name: string]: Case;
42
- };
43
- }
44
- declare function create<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
45
- [name: string]: DataTypeSchema;
46
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
47
- [name: string]: DataTypeSchema;
48
- }>, Memories extends {
23
+ declare function create<Case extends BoundAgent, I extends ExtractRunnableInputTypeIntersection<Case["runnable"]>, O extends ExtractRunnableOutputType<Case["runnable"]>, Memories extends {
49
24
  [name: string]: CreateRunnableMemory<I> & {
50
25
  /**
51
26
  * Whether this memory is primary? Primary memory will be passed as messages to LLM chat model,
@@ -55,7 +30,12 @@ declare function create<Case extends DecisionAgentCaseParameter, I extends Union
55
30
  */
56
31
  primary?: boolean;
57
32
  };
58
- }, State extends ContextState>({ context, ...options }: CreateLLMDecisionAgentOptions<Case, I, O, Memories, State>): LLMDecisionAgent<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, ExtractRunnableOutputType<Case["runnable"]>, {
33
+ }, State extends ContextState>({ context, ...options }: Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> & {
34
+ context: Context<State>;
35
+ cases: {
36
+ [name: string]: Case;
37
+ };
38
+ }): LLMDecisionAgent<OmitBoundAgentInput<Case, "ai">, ExtractRunnableOutputType<Case["runnable"]>, {
59
39
  [name in keyof Memories]: MemorableSearchOutput<Memories[name]["memory"]>;
60
40
  }, State>;
61
41
  export interface LLMDecisionAgentDefinition extends RunnableDefinition, Pick<LLMAgentDefinition, "modelOptions" | "messages" | "primaryMemoryId"> {
@@ -69,5 +49,8 @@ export interface LLMDecisionCase {
69
49
  runnable?: {
70
50
  id?: string;
71
51
  };
52
+ input?: {
53
+ [inputId: string]: BindAgentInput;
54
+ };
72
55
  }
73
56
  export {};
@@ -8,7 +8,9 @@ export declare class OpenaiLLMModel extends LLMModel {
8
8
  private client;
9
9
  setApiKey(apiKey: string): void;
10
10
  process(input: LLMModelInputs): AsyncGenerator<{
11
- $text: string | undefined;
11
+ $text: string;
12
+ delta?: undefined;
13
+ } | {
12
14
  delta: {
13
15
  toolCalls: {
14
16
  id?: string;
@@ -19,5 +21,6 @@ export declare class OpenaiLLMModel extends LLMModel {
19
21
  };
20
22
  }[];
21
23
  };
24
+ $text?: undefined;
22
25
  }, void, unknown>;
23
26
  }
@@ -1,3 +1,23 @@
1
1
  import type { Runnable, RunnableResponseStream } from "../runnable";
2
+ import type { UnionToIntersection } from "./union";
2
3
  export type ExtractRunnableInputType<T> = T extends Runnable<infer I, any> ? I : never;
3
4
  export type ExtractRunnableOutputType<T> = T extends Runnable<any, infer O> ? Exclude<O, RunnableResponseStream<any>> : never;
5
+ export type ExtractRunnableInputTypeIntersection<T> = UnionToIntersection<ExtractRunnableInputType<T>, {}>;
6
+ export type ExtractRunnableOutputTypeIntersection<T> = UnionToIntersection<ExtractRunnableOutputType<T>, {}>;
7
+ export type BindAgentInput = {
8
+ from: "ai";
9
+ };
10
+ export type PickInputFrom<I, From extends BindAgentInput["from"]> = {
11
+ [key in keyof I as I[key] extends {
12
+ from: From;
13
+ } ? key : never]: I[key];
14
+ };
15
+ export type OmitBoundAgentInput<Case extends BoundAgent, From extends BindAgentInput["from"]> = Omit<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, keyof PickInputFrom<Required<UnionToIntersection<NonNullable<Case["input"]>, {}>>, From>>;
16
+ export type BindAgentInputs<R extends Runnable> = {
17
+ [key in keyof ExtractRunnableInputType<R>]?: BindAgentInput;
18
+ };
19
+ export interface BoundAgent<R extends Runnable = Runnable, I extends BindAgentInputs<R> = BindAgentInputs<R>> {
20
+ description?: string;
21
+ runnable: R;
22
+ input?: I;
23
+ }
@@ -1,6 +1,7 @@
1
1
  import type { Context, ContextState } from "./context";
2
2
  import type { MemoryItemWithScore, MemoryMessage } from "./memorable";
3
3
  import { type RunOptions, Runnable, type RunnableResponse, type RunnableResponseChunk, type RunnableResponseStream } from "./runnable";
4
+ import type { BindAgentInputs, BoundAgent } from "./utils/runnable-type";
4
5
  export interface AgentProcessOptions<Memories extends {
5
6
  [name: string]: MemoryItemWithScore[];
6
7
  }> {
@@ -38,4 +39,13 @@ export declare abstract class Agent<I extends {
38
39
  */
39
40
  protected onResult(_result: O): Promise<void>;
40
41
  abstract process(input: I, options: AgentProcessOptions<Memories>): Promise<RunnableResponse<O> | AsyncGenerator<RunnableResponseChunk<O>, void>> | AsyncGenerator<RunnableResponseChunk<O>, void>;
42
+ /**
43
+ * Bind some inputs to the agent, used for process of `PipelineAgent` or case of `LLMDecisionAgent`.
44
+ * @param options The bind options.
45
+ * @returns The bound agent.
46
+ */
47
+ bind<Input extends BindAgentInputs<typeof this>>(options: {
48
+ description?: string;
49
+ input?: Input;
50
+ }): BoundAgent<typeof this, Readonly<Input>>;
41
51
  }
package/lib/esm/agent.js CHANGED
@@ -94,4 +94,15 @@ export class Agent extends Runnable {
94
94
  async onResult(_result) {
95
95
  // Override this method to perform additional operations before the result is returned
96
96
  }
97
+ /**
98
+ * Bind some inputs to the agent, used for process of `PipelineAgent` or case of `LLMDecisionAgent`.
99
+ * @param options The bind options.
100
+ * @returns The bound agent.
101
+ */
102
+ bind(options) {
103
+ return {
104
+ ...options,
105
+ runnable: this,
106
+ };
107
+ }
97
108
  }
@@ -1,14 +1,12 @@
1
1
  import { Agent, type AgentProcessOptions } from "./agent";
2
2
  import type { Context, ContextState } from "./context";
3
- import type { DataTypeSchema } from "./definitions/data-type-schema";
4
3
  import { type CreateRunnableMemory } from "./definitions/memory";
5
4
  import type { CreateLLMAgentOptions, LLMAgentDefinition } from "./llm-agent";
6
5
  import type { LLMModel } from "./llm-model";
7
6
  import type { MemorableSearchOutput, MemoryItemWithScore } from "./memorable";
8
- import type { Runnable, RunnableDefinition } from "./runnable";
7
+ import type { RunnableDefinition } from "./runnable";
9
8
  import { OrderedRecord } from "./utils";
10
- import type { ExtractRunnableInputType, ExtractRunnableOutputType } from "./utils/runnable-type";
11
- import type { UnionToIntersection } from "./utils/union";
9
+ import type { BindAgentInput, BoundAgent, ExtractRunnableInputTypeIntersection, ExtractRunnableOutputType, OmitBoundAgentInput } from "./utils/runnable-type";
12
10
  export declare class LLMDecisionAgent<I extends {
13
11
  [name: string]: any;
14
12
  } = {}, O extends {
@@ -22,30 +20,7 @@ export declare class LLMDecisionAgent<I extends {
22
20
  constructor(definition: LLMDecisionAgentDefinition, context?: Context<State>, model?: LLMModel | undefined);
23
21
  process(input: I, options: AgentProcessOptions<Memories>): Promise<import("./runnable").RunnableResponse<O>>;
24
22
  }
25
- export interface DecisionAgentCaseParameter<R extends Runnable = Runnable> {
26
- description?: string;
27
- runnable: R;
28
- }
29
- /**
30
- * Options to create LLMDecisionAgent.
31
- */
32
- export interface CreateLLMDecisionAgentOptions<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
33
- [name: string]: DataTypeSchema;
34
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
35
- [name: string]: DataTypeSchema;
36
- }>, Memories extends {
37
- [name: string]: CreateRunnableMemory<I>;
38
- }, State extends ContextState> extends Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> {
39
- context: Context<State>;
40
- cases: {
41
- [name: string]: Case;
42
- };
43
- }
44
- declare function create<Case extends DecisionAgentCaseParameter, I extends UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {
45
- [name: string]: DataTypeSchema;
46
- }>, O extends UnionToIntersection<ExtractRunnableOutputType<Case["runnable"]>, {
47
- [name: string]: DataTypeSchema;
48
- }>, Memories extends {
23
+ declare function create<Case extends BoundAgent, I extends ExtractRunnableInputTypeIntersection<Case["runnable"]>, O extends ExtractRunnableOutputType<Case["runnable"]>, Memories extends {
49
24
  [name: string]: CreateRunnableMemory<I> & {
50
25
  /**
51
26
  * Whether this memory is primary? Primary memory will be passed as messages to LLM chat model,
@@ -55,7 +30,12 @@ declare function create<Case extends DecisionAgentCaseParameter, I extends Union
55
30
  */
56
31
  primary?: boolean;
57
32
  };
58
- }, State extends ContextState>({ context, ...options }: CreateLLMDecisionAgentOptions<Case, I, O, Memories, State>): LLMDecisionAgent<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, ExtractRunnableOutputType<Case["runnable"]>, {
33
+ }, State extends ContextState>({ context, ...options }: Pick<CreateLLMAgentOptions<I, O, Memories, State>, "name" | "memories" | "messages" | "modelOptions"> & {
34
+ context: Context<State>;
35
+ cases: {
36
+ [name: string]: Case;
37
+ };
38
+ }): LLMDecisionAgent<OmitBoundAgentInput<Case, "ai">, ExtractRunnableOutputType<Case["runnable"]>, {
59
39
  [name in keyof Memories]: MemorableSearchOutput<Memories[name]["memory"]>;
60
40
  }, State>;
61
41
  export interface LLMDecisionAgentDefinition extends RunnableDefinition, Pick<LLMAgentDefinition, "modelOptions" | "messages" | "primaryMemoryId"> {
@@ -69,5 +49,8 @@ export interface LLMDecisionCase {
69
49
  runnable?: {
70
50
  id?: string;
71
51
  };
52
+ input?: {
53
+ [inputId: string]: BindAgentInput;
54
+ };
72
55
  }
73
56
  export {};
@@ -17,6 +17,7 @@ import { TYPES } from "./constants";
17
17
  import { toRunnableMemories, } from "./definitions/memory";
18
18
  import { OrderedRecord, extractOutputsFromRunnableOutput, renderMessage, } from "./utils";
19
19
  import { prepareMessages } from "./utils/message-utils";
20
+ import { outputsToJsonSchema } from "./utils/structured-output-schema";
20
21
  let LLMDecisionAgent = class LLMDecisionAgent extends Agent {
21
22
  definition;
22
23
  model;
@@ -42,19 +43,23 @@ let LLMDecisionAgent = class LLMDecisionAgent extends Agent {
42
43
  const name = t.name || runnable.name;
43
44
  if (!name)
44
45
  throw new Error("Case name is required");
45
- return { name, description: t.description, runnable };
46
+ return { ...t, name, runnable };
46
47
  }));
47
48
  const llmInputs = {
48
49
  messages: messagesWithMemory,
49
50
  modelOptions: definition.modelOptions,
50
51
  tools: cases.map((t) => {
51
- // TODO: auto generate parameters by llm model if needed
52
+ // Filter inputs that are bound from AI
53
+ const inputsFromAI = OrderedRecord.fromArray(OrderedRecord.filter(t.runnable.definition.inputs, (i) => t.input?.[i.id]?.from === "ai"));
54
+ const parameters = inputsFromAI.$indexes.length > 0
55
+ ? outputsToJsonSchema(inputsFromAI)
56
+ : {};
52
57
  return {
53
58
  type: "function",
54
59
  function: {
55
60
  name: t.name,
56
61
  description: t.description,
57
- parameters: {},
62
+ parameters,
58
63
  },
59
64
  };
60
65
  }),
@@ -62,14 +67,16 @@ let LLMDecisionAgent = class LLMDecisionAgent extends Agent {
62
67
  };
63
68
  const { toolCalls } = await model.run(llmInputs);
64
69
  // TODO: support run multiple calls
65
- const functionNameToCall = toolCalls?.[0]?.function?.name;
70
+ const { name: functionNameToCall, arguments: args } = toolCalls?.[0]?.function ?? {};
66
71
  if (!functionNameToCall)
67
72
  throw new Error("No any runnable called");
68
73
  const caseToCall = cases.find((i) => i.name === functionNameToCall);
69
74
  if (!caseToCall)
70
75
  throw new Error("Case not found");
76
+ // Prepare arguments generated by LLM model
77
+ const llmArgs = args ? JSON.parse(args) : {};
71
78
  // TODO: check result structure and omit undefined values
72
- const output = await caseToCall.runnable.run(input, { stream: true });
79
+ const output = await caseToCall.runnable.run({ ...input, ...llmArgs }, { stream: true });
73
80
  return extractOutputsFromRunnableOutput(output, ({ $text, ...json }) => this.updateMemories([
74
81
  ...originalMessages,
75
82
  {
@@ -89,12 +96,22 @@ LLMDecisionAgent = __decorate([
89
96
  export { LLMDecisionAgent };
90
97
  function create({ context, ...options }) {
91
98
  const agentId = options.name || nanoid();
92
- const cases = OrderedRecord.fromArray(Object.entries(options.cases).map(([name, c]) => ({
93
- id: nanoid(),
94
- name: name || c.runnable.name,
95
- description: c.description,
96
- runnable: { id: c.runnable.id },
97
- })));
99
+ const cases = OrderedRecord.fromArray(Object.entries(options.cases).map(([name, c]) => {
100
+ const bindInputs = Object.entries(c.input ?? {});
101
+ return {
102
+ id: nanoid(),
103
+ name: name || c.runnable.name,
104
+ description: c.description,
105
+ runnable: { id: c.runnable.id },
106
+ input: Object.fromEntries(bindInputs.map(([inputName, v]) => {
107
+ const input = c.runnable.definition.inputs[inputName] ||
108
+ OrderedRecord.find(c.runnable.definition.inputs, (i) => i.name === inputName);
109
+ if (!input)
110
+ throw new Error(`Input ${inputName} not found`);
111
+ return [input.id, v];
112
+ })),
113
+ };
114
+ }));
98
115
  const inputs = OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.inputs));
99
116
  const outputs = OrderedRecord.fromArray(OrderedRecord.map(OrderedRecord.merge(...Object.values(options.cases).map((i) => i.runnable.definition.outputs)), (o) => ({ ...o, required: false })));
100
117
  const memories = toRunnableMemories(agentId, inputs, options.memories ?? {});
@@ -8,7 +8,9 @@ export declare class OpenaiLLMModel extends LLMModel {
8
8
  private client;
9
9
  setApiKey(apiKey: string): void;
10
10
  process(input: LLMModelInputs): AsyncGenerator<{
11
- $text: string | undefined;
11
+ $text: string;
12
+ delta?: undefined;
13
+ } | {
12
14
  delta: {
13
15
  toolCalls: {
14
16
  id?: string;
@@ -19,5 +21,6 @@ export declare class OpenaiLLMModel extends LLMModel {
19
21
  };
20
22
  }[];
21
23
  };
24
+ $text?: undefined;
22
25
  }, void, unknown>;
23
26
  }
@@ -37,22 +37,22 @@ export class OpenaiLLMModel extends LLMModel {
37
37
  const toolCalls = [];
38
38
  for await (const chunk of res) {
39
39
  const choice = chunk.choices?.[0];
40
- const calls = choice?.delta.tool_calls?.map((i) => ({
41
- id: i.id || nanoid(),
42
- type: "function",
43
- function: {
44
- name: i.function?.name,
45
- arguments: i.function?.arguments,
46
- },
47
- }));
48
- if (calls?.length) {
49
- toolCalls.push(...calls);
40
+ if (choice?.delta.tool_calls?.length) {
41
+ for (const call of choice.delta.tool_calls) {
42
+ const tool = toolCalls[call.index] ?? { id: call.id || nanoid() };
43
+ toolCalls[call.index] = tool;
44
+ if (call.type)
45
+ tool.type = call.type;
46
+ tool.function ??= {};
47
+ tool.function.name =
48
+ (tool.function.name || "") + (call.function?.name || "");
49
+ tool.function.arguments = (tool.function.arguments || "").concat(call.function?.arguments || "");
50
+ }
50
51
  }
51
- yield {
52
- $text: choice?.delta.content || undefined,
53
- delta: { toolCalls },
54
- };
52
+ if (choice?.delta.content)
53
+ yield { $text: choice.delta.content };
55
54
  }
55
+ yield { delta: { toolCalls } };
56
56
  }
57
57
  }
58
58
  async function contentsFromInputMessages(messages) {
@@ -1,3 +1,23 @@
1
1
  import type { Runnable, RunnableResponseStream } from "../runnable";
2
+ import type { UnionToIntersection } from "./union";
2
3
  export type ExtractRunnableInputType<T> = T extends Runnable<infer I, any> ? I : never;
3
4
  export type ExtractRunnableOutputType<T> = T extends Runnable<any, infer O> ? Exclude<O, RunnableResponseStream<any>> : never;
5
+ export type ExtractRunnableInputTypeIntersection<T> = UnionToIntersection<ExtractRunnableInputType<T>, {}>;
6
+ export type ExtractRunnableOutputTypeIntersection<T> = UnionToIntersection<ExtractRunnableOutputType<T>, {}>;
7
+ export type BindAgentInput = {
8
+ from: "ai";
9
+ };
10
+ export type PickInputFrom<I, From extends BindAgentInput["from"]> = {
11
+ [key in keyof I as I[key] extends {
12
+ from: From;
13
+ } ? key : never]: I[key];
14
+ };
15
+ export type OmitBoundAgentInput<Case extends BoundAgent, From extends BindAgentInput["from"]> = Omit<UnionToIntersection<ExtractRunnableInputType<Case["runnable"]>, {}>, keyof PickInputFrom<Required<UnionToIntersection<NonNullable<Case["input"]>, {}>>, From>>;
16
+ export type BindAgentInputs<R extends Runnable> = {
17
+ [key in keyof ExtractRunnableInputType<R>]?: BindAgentInput;
18
+ };
19
+ export interface BoundAgent<R extends Runnable = Runnable, I extends BindAgentInputs<R> = BindAgentInputs<R>> {
20
+ description?: string;
21
+ runnable: R;
22
+ input?: I;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/core",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "AIGNE core library",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,14 +24,14 @@
24
24
  "url": "git+https://github.com/blocklet/ai-studio.git"
25
25
  },
26
26
  "dependencies": {
27
+ "@google/generative-ai": "^0.21.0",
27
28
  "@types/mustache": "^4.2.5",
28
29
  "lodash": "^4.17.21",
29
30
  "mustache": "^4.2.0",
30
31
  "nanoid": "^5.0.9",
31
- "tsyringe": "^4.8.0",
32
- "ufo": "^1.5.4",
33
32
  "openai": "^4.79.1",
34
- "@google/generative-ai": "^0.21.0"
33
+ "tsyringe": "^4.8.0",
34
+ "ufo": "^1.5.4"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@google/generative-ai": "^0.21.0",