@autobe/agent
Version:
AI backend server code generator
142 lines (134 loc) • 4.42 kB
text/typescript
import {
IAgenticaHistoryJson,
MicroAgentica,
MicroAgenticaHistory,
} from "@agentica/core";
import {
AutoBeConsentFunctionCallEvent,
AutoBeEvent,
AutoBeEventSource,
} from "@autobe/interface";
import { StringUtil } from "@autobe/utils";
import { IPointer } from "tstl";
import typia from "typia";
import { v7 } from "uuid";
import { AutoBeConfigConstant } from "../constants/AutoBeConfigConstant";
import { AutoBeSystemPromptConstant } from "../constants/AutoBeSystemPromptConstant";
import { IAutoBeConfig } from "../structures/IAutoBeConfig";
import { IAutoBeVendor } from "../structures/IAutoBeVendor";
import { mergeSystemMessages } from "./mergeSystemMessages";
// import { supportFunctionCallFallback } from "./supportFunctionCallFallback";
import { supportMistral } from "./supportMistral";
/**
* Generates automatic consent messages when AI hesitates and seeks permission
* before function calling.
*
* Uses fast LLM (chatgpt) to analyze assistant messages and determine if
* they're seeking function execution approval. Returns strong directive consent
* message to break permission-seeking loops, or `null` if not applicable.
*
* @returns Consent message if applicable, `null` otherwise
*/
export const consentFunctionCall = async (props: {
dispatch: (event: AutoBeEvent) => void;
source: AutoBeEventSource;
config: IAutoBeConfig;
vendor: IAutoBeVendor;
assistantMessage: string; // trimmed
}): Promise<string | null> => {
const dispatch = (result: AutoBeConsentFunctionCallEvent.IResult | null) =>
props.dispatch({
type: "consentFunctionCall",
id: v7(),
source: props.source,
assistantMessage: props.assistantMessage,
result,
created_at: new Date().toISOString(),
});
if (props.assistantMessage.length === 0) {
const message: string = StringUtil.trim`
You sent me an empty assistant message.
Don't do such foolish thing again, and do the function calling properly.
`;
dispatch({
type: "consent",
message,
});
return message;
}
const pointer: IPointer<AutoBeConsentFunctionCallEvent.IResult | null> = {
value: null,
};
const agent: MicroAgentica = new MicroAgentica({
vendor: props.vendor,
config: {
...(props.config ?? []),
executor: {
describe: false,
},
retry: props.config?.retry ?? AutoBeConfigConstant.VALIDATION_RETRY,
// stream: false,
},
histories: [
{
id: v7(),
type: "systemMessage",
text: AutoBeSystemPromptConstant.CONSENT_FUNCTION_CALL,
created_at: new Date().toISOString(),
} satisfies IAgenticaHistoryJson.ISystemMessage,
{
id: v7(),
type: "assistantMessage",
text: props.assistantMessage,
created_at: new Date().toISOString(),
} satisfies IAgenticaHistoryJson.IAssistantMessage,
],
controllers: [
typia.llm.controller<IConsentApplication>("consent", {
consent: (props) => {
pointer.value = {
type: "consent",
message: props.message,
};
},
notApplicable: () => {
pointer.value = {
type: "notApplicable",
};
},
} satisfies IConsentApplication),
],
});
supportMistral(agent, props.vendor);
// supportFunctionCallFallback(agent, props.vendor);
mergeSystemMessages(agent, props.vendor);
const histories: MicroAgenticaHistory[] = await agent.conversate(
"Analyze and judge this assistant message please.",
);
if (pointer.value === null) {
const last: MicroAgenticaHistory | undefined =
histories[histories.length - 1];
if (last?.type === "assistantMessage")
pointer.value = {
type: "assistantMessage",
message: last.text,
};
}
dispatch(pointer.value);
return pointer.value?.type === "consent" ? pointer.value.message : null;
};
interface IConsentApplication {
/**
* Generate authoritative consent message when assistant seeks function
* execution approval.
*
* @param props.message Strong directive (1-2 sentences). E.g., "Execute
* immediately. Do not ask again."
*/
consent(props: { message: string }): void;
/**
* Indicate assistant message doesn't require function calling consent (e.g.,
* general conversation, asking for parameters, etc.).
*/
notApplicable(): void;
}