@autobe/agent
Version:
AI backend server code generator
201 lines (190 loc) • 5.86 kB
text/typescript
import {
IAgenticaController,
IAgenticaTokenUsageJson,
MicroAgentica,
} from "@agentica/core";
import {
AutoBeImageDescribeDraft,
AutoBeImageDescribeDraftEvent,
AutoBeProgressEventBase,
AutoBeUserConversateContent,
AutoBeUserImageConversateContent,
} from "@autobe/interface";
import { IPointer } from "tstl";
import typia, { ILlmApplication, IValidation } from "typia";
import { v7 } from "uuid";
import { AutoBeContext } from "../../context/AutoBeContext";
import { createAutoBeUserMessageContent } from "../../factory/createAutoBeMessageContent";
import { mergeSystemMessages } from "../../factory/mergeSystemMessages";
// import { supportFunctionCallFallback } from "../../factory/supportFunctionCallFallback";
import { supportMistral } from "../../factory/supportMistral";
import { executeCachedBatch } from "../../utils/executeCachedBatch";
import { transformImageDescribeDraftHistories } from "./histories/transformImageDescribeDraftHistories";
import { IAutoBeImageDescribeDraftApplication } from "./structures/IAutoBeImageDescribeDraftApplication";
export const orchestrateImageDescribeDrafts = async (
ctx: AutoBeContext,
props: {
content: AutoBeUserConversateContent[];
},
): Promise<AutoBeImageDescribeDraft[]> => {
const [imageContents, otherContents] = props.content.reduce(
(acc, cur) => {
if (cur.type === "image") {
acc[0].push(cur);
} else {
acc[1].push(cur);
}
return acc;
},
[
[] as AutoBeUserImageConversateContent[],
[] as AutoBeUserConversateContent[],
],
);
const progress: AutoBeProgressEventBase = {
total: imageContents.length,
completed: 0,
};
// Process each image individually
const drafts: (AutoBeImageDescribeDraft | null)[] = await executeCachedBatch(
ctx,
imageContents.map((imageContent) => async (promptCacheKey) => {
try {
const event: AutoBeImageDescribeDraftEvent = await process(ctx, {
imageContents: [imageContent], // Single image
userContents: otherContents,
progress,
promptCacheKey,
});
ctx.dispatch(event);
return {
...event,
image: imageContent,
description: event.draft,
};
} catch {
return null;
}
}),
);
return drafts.filter((d) => d !== null);
};
async function process(
ctx: AutoBeContext,
props: {
imageContents: AutoBeUserImageConversateContent[];
userContents: AutoBeUserConversateContent[];
progress: AutoBeProgressEventBase;
promptCacheKey: string;
},
): Promise<AutoBeImageDescribeDraftEvent> {
const pointer: IPointer<IAutoBeImageDescribeDraftApplication.IProps | null> =
{
value: null,
};
const content: AutoBeUserConversateContent[] = [
...props.imageContents,
...props.userContents,
];
const agent: MicroAgentica = new MicroAgentica({
vendor: ctx.vendor,
config: {
executor: {
describe: false,
},
retry: ctx.retry,
// stream: false,
},
histories: transformImageDescribeDraftHistories(),
controllers: [
createController({
build: (next) => {
pointer.value = next;
},
}),
],
});
supportMistral(agent, {
api: ctx.vendor.api,
model: ctx.vendor.model,
options: ctx.vendor.options,
semaphore:
typeof ctx.vendor.semaphore === "number"
? ctx.vendor.semaphore
: ctx.vendor.semaphore?.max(),
});
// supportFunctionCallFallback(agent, {
// api: ctx.vendor.api,
// model: ctx.vendor.model,
// options: ctx.vendor.options,
// semaphore:
// typeof ctx.vendor.semaphore === "number"
// ? ctx.vendor.semaphore
// : ctx.vendor.semaphore?.max(),
// });
mergeSystemMessages(agent, {
api: ctx.vendor.api,
model: ctx.vendor.model,
options: ctx.vendor.options,
semaphore:
typeof ctx.vendor.semaphore === "number"
? ctx.vendor.semaphore
: ctx.vendor.semaphore?.max(),
});
agent.on("request", (e) => {
if (!!e.body.tools?.length) {
e.body.tool_choice = "required";
}
});
await agent.conversate(
content.map((c) => createAutoBeUserMessageContent({ content: c })),
);
const tokenUsage: IAgenticaTokenUsageJson.IComponent = agent
.getTokenUsage()
.toJSON().aggregate;
ctx.usage().record(tokenUsage, ["facade"]);
props.progress.completed += props.imageContents.length;
if (pointer.value === null) throw new Error("Failed to analyze image.");
const event: AutoBeImageDescribeDraftEvent = {
type: "imageDescribeDraft",
id: v7(),
observation: pointer.value.observation,
analysis: pointer.value.analysis,
topics: pointer.value.topics,
summary: pointer.value.summary,
draft: pointer.value.description,
completed: props.progress.completed,
tokenUsage,
total: props.progress.total,
created_at: new Date().toISOString(),
};
return event;
}
function createController(props: {
build: (next: IAutoBeImageDescribeDraftApplication.IProps) => void;
}): IAgenticaController.IClass {
const validate = (
next: unknown,
): IValidation<IAutoBeImageDescribeDraftApplication.IProps> => {
const result: IValidation<IAutoBeImageDescribeDraftApplication.IProps> =
typia.validate<IAutoBeImageDescribeDraftApplication.IProps>(next);
if (result.success === false) return result;
return result;
};
const application: ILlmApplication =
typia.llm.application<IAutoBeImageDescribeDraftApplication>({
validate: {
analyzeImage: validate,
},
});
return {
protocol: "class",
name: "image",
application,
execute: {
analyzeImage: (next) => {
props.build(next);
},
} satisfies IAutoBeImageDescribeDraftApplication,
};
}