Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support multi agent for ts #300

Merged
merged 32 commits into from
Sep 26, 2024
Merged
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
413593b
feat: update question to ask creating multiagent in express
thucpn Sep 18, 2024
622b84b
feat: add express simple multiagent
thucpn Sep 18, 2024
f464b40
fix: import from agent
thucpn Sep 18, 2024
0ebcb9f
Create yellow-jokes-protect.md
marcusschiesser Sep 19, 2024
f43f00a
create workflow with example agents
thucpn Sep 19, 2024
6c05872
remove unused files
thucpn Sep 19, 2024
2c7a538
update doc
thucpn Sep 19, 2024
5daf519
feat: streaming event
thucpn Sep 19, 2024
b875618
fix: streaming final result
thucpn Sep 19, 2024
b030a3d
fix: pipe final streaming result
thucpn Sep 19, 2024
33ce593
feat: funtional calling agent
thucpn Sep 20, 2024
de5ba29
fix: let default max attempt 2
thucpn Sep 20, 2024
aff4f0c
fix lint
thucpn Sep 20, 2024
c4041e2
refactor: move workflow folder to src
thucpn Sep 20, 2024
f659721
refactor: share settings file for ts templates
thucpn Sep 20, 2024
54d74f8
fix: move settings.ts to setting folder
thucpn Sep 20, 2024
d69cd42
refactor: move workflow to components
thucpn Sep 20, 2024
054ee5b
Update templates/components/multiagent/typescript/workflow/index.ts
marcusschiesser Sep 23, 2024
7297edf
create ts multi agent from streaming template
thucpn Sep 23, 2024
3ebc3ec
remove copy express template
thucpn Sep 23, 2024
8cfabc5
enhance streaming and add handle tool call step
thucpn Sep 23, 2024
305296b
update changeset
thucpn Sep 23, 2024
ea3bbcf
refactor: code review
thucpn Sep 25, 2024
325c7ca
fix: coderabbit comment
thucpn Sep 25, 2024
45f7529
enable multiagent ts test
thucpn Sep 25, 2024
234b15e
fix: e2e apptype for nextjs
thucpn Sep 25, 2024
32c3d89
refactor: use context write event instead of append data annotation d…
thucpn Sep 25, 2024
7079b68
fix streaming
marcusschiesser Sep 25, 2024
6ecd5f8
Merge branch 'main' into feat/support-multi-agent-for-ts
marcusschiesser Sep 26, 2024
0679c37
fix: writer is just streaming
marcusschiesser Sep 26, 2024
fa45102
fix: clearly separate streaming events and content and use workflowEv…
marcusschiesser Sep 26, 2024
2fb502e
fix: add correct tool calls for tool messages
marcusschiesser Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 35 additions & 49 deletions templates/components/multiagent/typescript/workflow/single-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
Settings,
ToolCall,
ToolCallLLM,
ToolCallLLMMessageOptions,
callTool,
} from "llamaindex";
import { AgentInput, AgentRunEvent } from "./type";

Expand All @@ -33,7 +35,6 @@ export class FunctionCallingAgent extends Workflow {
systemPrompt?: string;
writeEvents: boolean;
role?: string;
toolCalled: boolean = false;

constructor(options: {
name: string;
Expand Down Expand Up @@ -75,26 +76,18 @@ export class FunctionCallingAgent extends Workflow {
}

private get chatHistory() {
return this.memory.getAllMessages();
}

private get toolsByName() {
return this.tools.reduce((acc: Record<string, BaseToolWithCall>, tool) => {
acc[tool.metadata.name] = tool;
return acc;
}, {});
return this.memory.getMessages();
}

private async prepareChatHistory(
ctx: Context,
ev: StartEvent<AgentInput>,
): Promise<InputEvent> {
this.toolCalled = false;
const { message, streaming } = ev.data.input;
ctx.set("streaming", streaming);
this.writeEvent(`Start to work on: ${message}`, ctx);
if (this.systemPrompt) {
this.memory.put({ role: "assistant", content: this.systemPrompt });
this.memory.put({ role: "system", content: this.systemPrompt });
}
this.memory.put({ role: "user", content: message });
return new InputEvent({ input: this.chatHistory });
Expand All @@ -112,8 +105,10 @@ export class FunctionCallingAgent extends Workflow {
messages: this.chatHistory,
tools: this.tools,
});
this.memory.put(result.message);

const toolCalls = this.getToolCallsFromResponse(result);
if (toolCalls.length && !this.toolCalled) {
if (toolCalls.length) {
return new ToolCallEvent({ toolCalls });
}
this.writeEvent("Finished task", ctx);
Expand Down Expand Up @@ -151,7 +146,8 @@ export class FunctionCallingAgent extends Workflow {
if (fullResponse) {
memory.put({
role: "assistant",
content: fullResponse.delta,
content: "",
options: fullResponse.options,
});
yield fullResponse;
}
Expand All @@ -162,7 +158,7 @@ export class FunctionCallingAgent extends Workflow {
if (isToolCall.value) {
const fullResponse = await generator.next();
const toolCalls = this.getToolCallsFromResponse(
fullResponse.value as ChatResponseChunk<object>,
fullResponse.value as ChatResponseChunk<ToolCallLLMMessageOptions>,
);
return new ToolCallEvent({ toolCalls });
}
Expand All @@ -175,48 +171,38 @@ export class FunctionCallingAgent extends Workflow {
ctx: Context,
ev: ToolCallEvent,
): Promise<InputEvent> {
this.toolCalled = true;
const { toolCalls } = ev.data;

const toolMsgs: ChatMessage[] = [];
for (const toolCall of toolCalls) {
const tool = this.toolsByName[toolCall.name];
const options = {
tool_call_id: toolCall.id,
name: tool.metadata.name,
};
if (!tool) {
toolMsgs.push({
role: "assistant",
content: `Tool ${toolCall.name} does not exist`,
options,
});
continue;
}

try {
const toolInput = JSON.parse(toolCall.input.toString());
const toolOutput = await tool.call(toolInput);
toolMsgs.push({
role: "assistant",
content: toolOutput.toString(),
options,
});
} catch (e) {
console.error(e);
toolMsgs.push({
role: "assistant",
content: `Encountered error in tool call: ${toolCall.name}`,
options,
});
}
for (const call of toolCalls) {
const targetTool = this.tools.find(
(tool) => tool.metadata.name === call.name,
);
// TODO: make logger optional in callTool in framework
const toolOutput = await callTool(targetTool, call, {
log: () => {},
error: console.error.bind(console),
warn: () => {},
});
Comment on lines +183 to +187
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@himself65 using callTool from the framework is very helpful, but would be nice if the logger object would be optional

toolMsgs.push({
content: JSON.stringify(toolOutput.output),
role: "user",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is role: "user" correct? It looks to me that the role should be tool or assistant?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it will be transformed by LITS

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was also confusing me

options: {
toolResult: {
result: toolOutput.output,
isError: toolOutput.isError,
id: call.id,
},
},
});
}

for (const msg of toolMsgs) {
this.memory.put(msg);
}

return new InputEvent({ input: this.memory.getAllMessages() });
return new InputEvent({ input: this.memory.getMessages() });
}

private writeEvent(msg: string, context: Context) {
Expand All @@ -231,10 +217,10 @@ export class FunctionCallingAgent extends Workflow {
if (!supportToolCall) throw new Error("LLM does not support tool calls");
}

// TODO: in LITS, llm should have a method to get tool calls from response
// then we don't need to use toolCalled flag
private getToolCallsFromResponse(
response: ChatResponse<object> | ChatResponseChunk<object>,
response:
| ChatResponse<ToolCallLLMMessageOptions>
| ChatResponseChunk<ToolCallLLMMessageOptions>,
): ToolCall[] {
let options;
if ("message" in response) {
Expand Down
Loading