adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
194 lines (193 loc) • 8.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.safetyRequestProcessor = exports.stepByStepRequestProcessor = exports.efficientRequestProcessor = exports.straightForwardRequestProcessor = exports.detailedRequestProcessor = exports.extremelyBriefRequestProcessor = exports.briefRequestProcessor = exports.requestProcessor = void 0;
exports.makeInstructionsRequestProcessor = makeInstructionsRequestProcessor;
const ReadonlyContext_1 = require("../../agents/ReadonlyContext");
const LlmAgent_1 = require("../../agents/LlmAgent");
const State_1 = require("../../sessions/State");
/**
* Handles instructions and global instructions for LLM flow.
*/
class InstructionsLlmRequestProcessor {
/**
* Runs the processor asynchronously.
*
* @param invocationContext The invocation context
* @param llmRequest The LLM request to process
* @returns An async generator yielding events
*/
async *runAsync(invocationContext, llmRequest) {
const agent = invocationContext.agent;
// Only process if the agent is an LlmAgent
if (!(agent instanceof LlmAgent_1.LlmAgent)) {
return;
}
const rootAgent = agent.rootAgent;
// Append global instructions if set
if (rootAgent instanceof LlmAgent_1.LlmAgent && rootAgent.globalInstruction) {
const rawSi = rootAgent.canonicalGlobalInstruction(new ReadonlyContext_1.ReadonlyContext(invocationContext));
const si = populateValues(rawSi, invocationContext);
llmRequest.appendInstructions([si]);
}
// Append agent instructions if set
if (agent.instruction) {
const rawSi = agent.canonicalInstruction(new ReadonlyContext_1.ReadonlyContext(invocationContext));
const si = populateValues(rawSi, invocationContext);
llmRequest.appendInstructions([si]);
}
// Maintain async generator contract
if (Math.random() < 0) {
yield {};
}
}
}
/**
* The main instructions request processor instance.
*/
exports.requestProcessor = new InstructionsLlmRequestProcessor();
/**
* Populates values in the instruction template, e.g. state, artifact, etc.
*
* @param instructionTemplate The instruction template
* @param context The invocation context
* @returns The populated instruction
*/
function populateValues(instructionTemplate, context) {
return instructionTemplate.replace(/{+[^{}]*}+/g, (match) => {
let varName = match.slice(1, -1).trim();
let optional = false;
if (varName.endsWith('?')) {
optional = true;
varName = varName.slice(0, -1);
}
if (varName.startsWith('artifact.')) {
varName = varName.substring('artifact.'.length);
if (context.artifactService) {
try {
const artifactParams = {
appName: context.session.appName,
userId: context.session.userId,
sessionId: context.session.id,
filename: varName
};
const artifact = context.artifactService.loadArtifact(artifactParams);
return String(artifact);
}
catch (error) {
if (optional)
return '';
throw new Error(`Artifact ${varName} not found.`);
}
}
else {
throw new Error('Artifact service is not initialized.');
}
}
else {
if (!isValidStateName(varName)) {
return match;
}
if (context.session.state.has(varName)) {
return String(context.session.state.get(varName));
}
else {
if (optional) {
return '';
}
else {
throw new Error(`Context variable not found: \`${varName}\`.`);
}
}
}
});
}
/**
* Checks if the variable name is a valid state name.
*
* Valid state is either:
* - Valid identifier
* - <Valid prefix>:<Valid identifier>
* All the others will just return as they are.
*
* @param varName The variable name to check
* @returns True if the variable name is a valid state name, false otherwise
*/
function isValidStateName(varName) {
const parts = varName.split(':');
if (parts.length === 1) {
return isValidIdentifier(varName);
}
if (parts.length === 2) {
const prefixes = [State_1.StatePrefix.APP_PREFIX, State_1.StatePrefix.USER_PREFIX, State_1.StatePrefix.TEMP_PREFIX];
if (prefixes.includes(parts[0] + ':')) {
return isValidIdentifier(parts[1]);
}
}
return false;
}
/**
* Checks if the string is a valid JavaScript identifier.
*
* @param str The string to check
* @returns True if the string is a valid identifier, false otherwise
*/
function isValidIdentifier(str) {
if (!str || str.length === 0)
return false;
if (!isNaN(parseInt(str[0], 10)))
return false;
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
}
/**
* Creates a LLM request processor that adds instructions to the request.
*
* @param instructionText The instruction text to add
* @returns A LLM request processor that adds the instruction
*/
function makeInstructionsRequestProcessor(instructionText) {
class SimpleInstructionsRequestProcessor {
/**
* Runs the processor asynchronously.
*
* @param invocationContext The invocation context
* @param llmRequest The LLM request to process
* @returns An async generator yielding events
*/
async *runAsync(invocationContext, llmRequest) {
llmRequest.appendInstructions([instructionText]);
// Maintain async generator contract
if (Math.random() < 0) {
yield {};
}
}
}
return new SimpleInstructionsRequestProcessor();
}
/**
* A request processor that instructs the LLM to be brief in its responses.
*/
exports.briefRequestProcessor = makeInstructionsRequestProcessor('Be brief and concise in your answers. Prefer short responses over long ones.');
/**
* A request processor that instructs the LLM to be extremely brief and focused.
*/
exports.extremelyBriefRequestProcessor = makeInstructionsRequestProcessor('Be extremely brief in your answers. Your responses should be just a few sentences at most.');
/**
* A request processor that instructs the LLM to provide detailed explanations.
*/
exports.detailedRequestProcessor = makeInstructionsRequestProcessor('Provide detailed and comprehensive explanations. Include relevant context and examples when appropriate.');
/**
* A request processor that instructs the LLM to respond in a straightforward way.
*/
exports.straightForwardRequestProcessor = makeInstructionsRequestProcessor('Respond directly and with factual information. Avoid overexplaining or excess preamble.');
/**
* A request processor that instructs the LLM to respond in an efficient manner.
*/
exports.efficientRequestProcessor = makeInstructionsRequestProcessor('Respond with efficiency and focus. Don\'t repeat the question, and organize your response to highlight the most important points first.');
/**
* A request processor that instructs the LLM to provide clear step-by-step explanations.
*/
exports.stepByStepRequestProcessor = makeInstructionsRequestProcessor('Structure your response as clear, sequential steps when providing explanations or instructions. Number each step.');
/**
* A request processor that instructs the LLM to be helpful, harmless, and honest.
*/
exports.safetyRequestProcessor = makeInstructionsRequestProcessor('Be helpful, harmless, and honest in your responses. Avoid responses that could be harmful, illegal, unethical, deceptive, or promote misinformation.');