Skip to content

Commit

Permalink
feat: add verbose mode to Agent (#800)
Browse files Browse the repository at this point in the history
  • Loading branch information
himself65 authored May 3, 2024
1 parent ee719a1 commit 2008efe
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-ducks-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---

feat: add verbose mode to Agent
1 change: 1 addition & 0 deletions examples/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEBUG=llamaindex
2 changes: 1 addition & 1 deletion examples/agent/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async function main() {
message: "How much is 5 + 5? then divide by 2",
});

console.log(String(response));
console.log(response.response.message);
}

void main().then(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ class GlobalSettings implements Config {
get debug() {
const debug = getEnv("DEBUG");
return (
getEnv("NODE_ENV") === "development" &&
Boolean(debug) &&
debug?.includes("llamaindex")
(Boolean(debug) && debug?.includes("llamaindex")) ||
debug === "*" ||
debug === "true"
);
}

Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/agent/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class AnthropicAgent extends AgentRunner<Anthropic> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}

Expand Down Expand Up @@ -94,7 +95,11 @@ export class AnthropicAgent extends AgentRunner<Anthropic> {
const targetTool = tools.find(
(tool) => tool.metadata.name === toolCall.name,
);
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.toolOutputs.push(toolOutput);
step.context.store.messages = [
...step.context.store.messages,
Expand Down
39 changes: 33 additions & 6 deletions packages/core/src/agent/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
pipeline,
randomUUID,
} from "@llamaindex/env";
import { Settings } from "../Settings.js";
import {
type ChatEngine,
type ChatEngineParamsNonStreaming,
type ChatEngineParamsStreaming,
} from "../engines/chat/index.js";
import { wrapEventCaller } from "../internal/context/EventCaller.js";
import { consoleLogger, emptyLogger } from "../internal/logger.js";
import { getCallbackManager } from "../internal/settings/CallbackManager.js";
import { isAsyncIterable } from "../internal/utils.js";
import type {
Expand Down Expand Up @@ -66,6 +68,7 @@ export function createTaskOutputStream<
const enqueueOutput = (
output: TaskStepOutput<Model, Store, AdditionalMessageOptions>,
) => {
context.logger.log("Enqueueing output for step(id, %s).", step.id);
taskOutputs.push(output);
controller.enqueue(output);
};
Expand All @@ -75,7 +78,9 @@ export function createTaskOutputStream<
},
});

context.logger.log("Starting step(id, %s).", step.id);
await handler(step, enqueueOutput);
context.logger.log("Finished step(id, %s).", step.id);
// fixme: support multi-thread when there are multiple outputs
// todo: for now we pretend there is only one task output
const { isLast, taskStep } = taskOutputs[0];
Expand All @@ -87,6 +92,10 @@ export function createTaskOutputStream<
toolCallCount: 1,
};
if (isLast) {
context.logger.log(
"Final step(id, %s) reached, closing task.",
step.id,
);
getCallbackManager().dispatchEvent("agent-end", {
payload: {
endStep: step,
Expand Down Expand Up @@ -125,6 +134,7 @@ export type AgentRunnerParams<
tools:
| BaseToolWithCall[]
| ((query: MessageContent) => Promise<BaseToolWithCall[]>);
verbose: boolean;
};

export type AgentParamsBase<
Expand All @@ -139,6 +149,7 @@ export type AgentParamsBase<
llm?: AI;
chatHistory?: ChatMessage<AdditionalMessageOptions>[];
systemPrompt?: MessageContent;
verbose?: boolean;
};

/**
Expand Down Expand Up @@ -218,6 +229,7 @@ export abstract class AgentRunner<
readonly #systemPrompt: MessageContent | null = null;
#chatHistory: ChatMessage<AdditionalMessageOptions>[];
readonly #runner: AgentWorker<AI, Store, AdditionalMessageOptions>;
readonly #verbose: boolean;

// create extra store
abstract createStore(): Store;
Expand All @@ -229,14 +241,15 @@ export abstract class AgentRunner<
protected constructor(
params: AgentRunnerParams<AI, Store, AdditionalMessageOptions>,
) {
const { llm, chatHistory, runner, tools } = params;
const { llm, chatHistory, systemPrompt, runner, tools, verbose } = params;
this.#llm = llm;
this.#chatHistory = chatHistory;
this.#runner = runner;
if (params.systemPrompt) {
this.#systemPrompt = params.systemPrompt;
if (systemPrompt) {
this.#systemPrompt = systemPrompt;
}
this.#tools = tools;
this.#verbose = verbose;
}

get llm() {
Expand All @@ -247,6 +260,10 @@ export abstract class AgentRunner<
return this.#chatHistory;
}

get verbose(): boolean {
return Settings.debug || this.#verbose;
}

public reset(): void {
this.#chatHistory = [];
}
Expand All @@ -270,8 +287,11 @@ export abstract class AgentRunner<
return task.context.toolCallCount < MAX_TOOL_CALLS;
}

// fixme: this shouldn't be async
async createTask(message: MessageContent, stream: boolean = false) {
createTask(
message: MessageContent,
stream: boolean = false,
verbose: boolean | undefined = undefined,
) {
const initialMessages = [...this.#chatHistory];
if (this.#systemPrompt !== null) {
const systemPrompt = this.#systemPrompt;
Expand All @@ -296,6 +316,13 @@ export abstract class AgentRunner<
toolOutputs: [] as ToolOutput[],
},
shouldContinue: AgentRunner.shouldContinue,
logger:
// disable verbose if explicitly set to false
verbose === false
? emptyLogger
: verbose || this.verbose
? consoleLogger
: emptyLogger,
});
}

Expand All @@ -312,7 +339,7 @@ export abstract class AgentRunner<
| AgentChatResponse<AdditionalMessageOptions>
| ReadableStream<AgentStreamChatResponse<AdditionalMessageOptions>>
> {
const task = await this.createTask(params.message, !!params.stream);
const task = this.createTask(params.message, !!params.stream);
const stepOutput = await pipeline(
task,
async (
Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/agent/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}

Expand Down Expand Up @@ -77,7 +78,11 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
const targetTool = tools.find(
(tool) => tool.metadata.name === toolCall.name,
);
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.toolOutputs.push(toolOutput);
step.context.store.messages = [
...step.context.store.messages,
Expand Down Expand Up @@ -154,7 +159,11 @@ export class OpenAIAgent extends AgentRunner<OpenAI> {
},
},
];
const toolOutput = await callTool(targetTool, toolCall);
const toolOutput = await callTool(
targetTool,
toolCall,
step.context.logger,
);
step.context.store.messages = [
...step.context.store.messages,
{
Expand Down
16 changes: 11 additions & 5 deletions packages/core/src/agent/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export class ReActAgent extends AgentRunner<LLM, ReACTAgentStore> {
"tools" in params
? params.tools
: params.toolRetriever.retrieve.bind(params.toolRetriever),
verbose: params.verbose ?? false,
});
}

Expand Down Expand Up @@ -387,14 +388,19 @@ export class ReActAgent extends AgentRunner<LLM, ReACTAgentStore> {
isLast: type !== "action",
});
});
step.context.logger.log("current reason: %O", reason);
step.context.store.reasons = [...step.context.store.reasons, reason];
if (reason.type === "action") {
const tool = tools.find((tool) => tool.metadata.name === reason.action);
const toolOutput = await callTool(tool, {
id: randomUUID(),
input: reason.input,
name: reason.action,
});
const toolOutput = await callTool(
tool,
{
id: randomUUID(),
input: reason.input,
name: reason.action,
},
step.context.logger,
);
step.context.store.reasons = [
...step.context.store.reasons,
{
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/agent/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ReadableStream } from "@llamaindex/env";
import type { Logger } from "../internal/logger.js";
import type { BaseEvent } from "../internal/type.js";
import type {
ChatMessage,
Expand Down Expand Up @@ -32,6 +33,7 @@ export type AgentTaskContext<
toolOutputs: ToolOutput[];
messages: ChatMessage<AdditionalMessageOptions>[];
} & Store;
logger: Readonly<Logger>;
};

export type TaskStep<
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/agent/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ReadableStream } from "@llamaindex/env";
import type { Logger } from "../internal/logger.js";
import { getCallbackManager } from "../internal/settings/CallbackManager.js";
import { isAsyncIterable, prettifyError } from "../internal/utils.js";
import type {
Expand All @@ -13,12 +14,14 @@ import type { BaseTool, JSONObject, JSONValue, ToolOutput } from "../types.js";
export async function callTool(
tool: BaseTool | undefined,
toolCall: ToolCall | PartialToolCall,
logger: Logger,
): Promise<ToolOutput> {
const input: JSONObject =
typeof toolCall.input === "string"
? JSON.parse(toolCall.input)
: toolCall.input;
if (!tool) {
logger.error(`Tool ${toolCall.name} does not exist.`);
const output = `Tool ${toolCall.name} does not exist.`;
return {
tool,
Expand All @@ -30,6 +33,9 @@ export async function callTool(
const call = tool.call;
let output: JSONValue;
if (!call) {
logger.error(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) does not have a implementation.`,
);
output = `Tool ${tool.metadata.name} (remote:${toolCall.name}) does not have a implementation.`;
return {
tool,
Expand All @@ -45,6 +51,10 @@ export async function callTool(
},
});
output = await call.call(tool, input);
logger.log(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) succeeded.`,
);
logger.log(`Output: ${JSON.stringify(output)}`);
const toolOutput: ToolOutput = {
tool,
input,
Expand All @@ -60,6 +70,9 @@ export async function callTool(
return toolOutput;
} catch (e) {
output = prettifyError(e);
logger.error(
`Tool ${tool.metadata.name} (remote:${toolCall.name}) failed: ${output}`,
);
}
return {
tool,
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/engines/chat/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export interface ChatEngineParamsBase {
* Optional chat history if you want to customize the chat history.
*/
chatHistory?: ChatMessage[] | ChatHistory;
/**
* Optional flag to enable verbose mode.
* @default false
*/
verbose?: boolean;
}

export interface ChatEngineParamsStreaming extends ChatEngineParamsBase {
Expand Down
17 changes: 17 additions & 0 deletions packages/core/src/internal/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type Logger = {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
};

export const emptyLogger: Logger = Object.freeze({
log: () => {},
error: () => {},
warn: () => {},
});

export const consoleLogger: Logger = Object.freeze({
log: console.log.bind(console),
error: console.error.bind(console),
warn: console.warn.bind(console),
});

0 comments on commit 2008efe

Please sign in to comment.