adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
153 lines (152 loc) • 5.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LanggraphAgent = void 0;
const Event_1 = require("../events/Event");
const BaseAgent_1 = require("./BaseAgent");
/**
* Extracts last human messages from given list of events.
*
* @param events The list of events
* @returns List of last human messages
*/
function getLastHumanMessages(events) {
const messages = [];
// Start from the end and work backwards
for (let i = events.length - 1; i >= 0; i--) {
const event = events[i];
// If we've found a message and the current event is not from the user, break
if (messages.length > 0 && event.author !== 'user') {
break;
}
// If it's a user message with content, add it
if (event.author === 'user' && event.content && event.content.parts && event.content.parts.length > 0) {
messages.unshift({
type: 'human',
content: event.content.parts[0].text,
});
}
}
return messages;
}
/**
* LangGraph agent implementation.
* Currently a concept implementation, supports single and multi-turn.
*/
class LanggraphAgent extends BaseAgent_1.BaseAgent {
/**
* Creates a new LanggraphAgent.
*
* @param name The name of the agent
* @param options Options for the agent
*/
constructor(name, options) {
super(name, options);
if (!options.graph) {
throw new Error('LanggraphAgent requires a graph');
}
this.graph = options.graph;
this.instruction = options.instruction || '';
}
/**
* Implementation of the agent's async invocation logic.
*
* @param ctx The invocation context
* @returns An async generator of events
*/
async *runAsyncImpl(ctx) {
// Needed for langgraph checkpointer (for subsequent invocations; multi-turn)
const config = { configurable: { thread_id: ctx.session.id } };
// Add instruction as SystemMessage if graph state is empty
const currentGraphState = this.graph.getState(config);
const graphMessages = currentGraphState.values?.messages || [];
let messages = [];
if (this.instruction && graphMessages.length === 0) {
messages.push({
type: 'system',
content: this.instruction,
});
}
// Add events to messages (evaluating the memory used; parent agent vs checkpointer)
messages = messages.concat(this.getMessages(ctx.session.events));
// Use the Runnable
const finalState = this.graph.invoke({ messages }, config);
const result = finalState.messages[finalState.messages.length - 1].content;
const resultEvent = new Event_1.Event({
invocationId: ctx.invocationId,
author: this.name,
branch: ctx.branch,
content: {
role: 'model',
parts: [{ text: result }],
},
});
yield resultEvent;
}
/**
* Implementation of the agent's live invocation logic.
*
* @param ctx The invocation context
* @returns An async generator of events
*/
async *runLiveImpl(ctx) {
// For live implementation, we simply delegate to the async implementation
yield* this.runAsyncImpl(ctx);
}
/**
* Sets the user content for the agent.
* This is a no-op for LanggraphAgent as it extracts content from session events.
*
* @param content The user content
* @param invocationContext The invocation context
*/
setUserContent(content, invocationContext) {
// LanggraphAgent doesn't need to store user content locally
// It will be extracted from session events
}
/**
* Extracts messages from given list of events.
*
* If the developer provides their own memory within langgraph, we return the
* last user messages only. Otherwise, we return all messages between the user
* and the agent.
*
* @param events The list of events
* @returns List of messages
*/
getMessages(events) {
if (this.graph.checkpointer) {
return getLastHumanMessages(events);
}
else {
return this.getConversationWithAgent(events);
}
}
/**
* Extracts conversation messages from given list of events.
*
* @param events The list of events
* @returns List of messages
*/
getConversationWithAgent(events) {
const messages = [];
for (const event of events) {
if (!event.content || !event.content.parts || event.content.parts.length === 0) {
continue;
}
if (event.author === 'user') {
messages.push({
type: 'human',
content: event.content.parts[0].text,
});
}
else if (event.author === this.name) {
messages.push({
type: 'ai',
content: event.content.parts[0].text,
});
}
}
return messages;
}
}
exports.LanggraphAgent = LanggraphAgent;