feat: new mastra
This commit is contained in:
parent
507a006b9f
commit
5d46a6cd34
17 changed files with 1197 additions and 937 deletions
|
|
@ -20,7 +20,7 @@ import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/s
|
|||
import { MastraAgent } from '@ag-ui/mastra';
|
||||
import { MastraService } from '@gitroom/nestjs-libraries/chat/mastra.service';
|
||||
import { Request, Response } from 'express';
|
||||
import { RuntimeContext } from '@mastra/core/di';
|
||||
import { RequestContext } from '@mastra/core/di';
|
||||
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
|
||||
import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/permissions/permission.exception.class';
|
||||
|
||||
|
|
@ -72,20 +72,19 @@ export class CopilotController {
|
|||
return;
|
||||
}
|
||||
const mastra = await this._mastraService.mastra();
|
||||
const runtimeContext = new RuntimeContext<ChannelsContext>();
|
||||
runtimeContext.set(
|
||||
const requestContext = new RequestContext<ChannelsContext>();
|
||||
requestContext.set(
|
||||
'integrations',
|
||||
req?.body?.variables?.properties?.integrations || []
|
||||
);
|
||||
|
||||
runtimeContext.set('organization', JSON.stringify(organization));
|
||||
runtimeContext.set('ui', 'true');
|
||||
requestContext.set('organization', JSON.stringify(organization));
|
||||
requestContext.set('ui', 'true');
|
||||
|
||||
const agents = MastraAgent.getLocalAgents({
|
||||
resourceId: organization.id,
|
||||
mastra,
|
||||
// @ts-ignore
|
||||
runtimeContext,
|
||||
requestContext: requestContext as any,
|
||||
});
|
||||
|
||||
const runtime = new CopilotRuntime({
|
||||
|
|
@ -124,7 +123,7 @@ export class CopilotController {
|
|||
const mastra = await this._mastraService.mastra();
|
||||
const memory = await mastra.getAgent('postiz').getMemory();
|
||||
try {
|
||||
return await memory.query({
|
||||
return await memory.recall({
|
||||
resourceId: organization.id,
|
||||
threadId,
|
||||
});
|
||||
|
|
@ -137,14 +136,12 @@ export class CopilotController {
|
|||
@CheckPolicies([AuthorizationActions.Create, Sections.AI])
|
||||
async getList(@GetOrgFromRequest() organization: Organization) {
|
||||
const mastra = await this._mastraService.mastra();
|
||||
// @ts-ignore
|
||||
const memory = await mastra.getAgent('postiz').getMemory();
|
||||
const list = await memory.getThreadsByResourceIdPaginated({
|
||||
resourceId: organization.id,
|
||||
const list = await memory.listThreads({
|
||||
filter: { resourceId: organization.id },
|
||||
perPage: 100000,
|
||||
page: 0,
|
||||
orderBy: 'createdAt',
|
||||
sortDirection: 'DESC',
|
||||
orderBy: { field: 'createdAt', direction: 'DESC' },
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { startMcp } from '@gitroom/nestjs-libraries/chat/start.mcp';
|
|||
async function start() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
rawBody: true,
|
||||
bodyParser: false,
|
||||
cors: {
|
||||
...(!process.env.NOT_SECURED ? { credentials: true } : {}),
|
||||
allowedHeaders: [
|
||||
|
|
@ -52,8 +53,12 @@ async function start() {
|
|||
})
|
||||
);
|
||||
|
||||
app.use(['/copilot/*', '/posts'], (req: any, res: any, next: any) => {
|
||||
json({ limit: '50mb' })(req, res, next);
|
||||
app.use((req: any, res: any, next: any) => {
|
||||
if (req.path.startsWith('/mcp') || req.path.startsWith('/sse/') || req.path.startsWith('/message/')) {
|
||||
return next();
|
||||
}
|
||||
const limit = ['/copilot/', '/posts'].some((p) => req.path.startsWith(p)) ? '50mb' : '100kb';
|
||||
json({ limit })(req, res, next);
|
||||
});
|
||||
|
||||
app.use(cookieParser());
|
||||
|
|
|
|||
|
|
@ -1,16 +1,6 @@
|
|||
import type { ZodLikeSchema } from '@mastra/core/dist/types/zod-compat';
|
||||
import type {
|
||||
ToolExecutionContext,
|
||||
} from '@mastra/core/dist/tools/types';
|
||||
import { Tool } from '@mastra/core/dist/tools/tool';
|
||||
import type { ToolAction } from '@mastra/core/tools';
|
||||
|
||||
export type ToolReturn = Tool<
|
||||
ZodLikeSchema,
|
||||
ZodLikeSchema,
|
||||
ZodLikeSchema,
|
||||
ZodLikeSchema,
|
||||
ToolExecutionContext<ZodLikeSchema, ZodLikeSchema, ZodLikeSchema>
|
||||
>;
|
||||
export type ToolReturn = ToolAction<any, any, any, any, any, any>;
|
||||
|
||||
export interface AgentToolInterface {
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
import { ToolAction } from '@mastra/core/dist/tools/types';
|
||||
import { getAuth } from '@gitroom/nestjs-libraries/chat/async.storage';
|
||||
|
||||
export const checkAuth: ToolAction['execute'] = async (
|
||||
{ runtimeContext },
|
||||
options
|
||||
export const checkAuth = (
|
||||
inputData: any,
|
||||
context: any
|
||||
) => {
|
||||
const auth = getAuth();
|
||||
// @ts-ignore
|
||||
if (options?.extra?.authInfo || auth) {
|
||||
runtimeContext.set(
|
||||
// @ts-ignore
|
||||
const authInfo = context?.mcp?.extra?.authInfo || auth;
|
||||
if (authInfo && context?.requestContext) {
|
||||
(context.requestContext as any).set(
|
||||
'organization',
|
||||
// @ts-ignore
|
||||
JSON.stringify(options?.extra?.authInfo || auth)
|
||||
JSON.stringify(authInfo)
|
||||
);
|
||||
// @ts-ignore
|
||||
runtimeContext.set('ui', 'false');
|
||||
(context.requestContext as any).set('ui', 'false');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,10 +43,11 @@ export class LoadToolsService {
|
|||
async agent() {
|
||||
const tools = await this.loadTools();
|
||||
return new Agent({
|
||||
id: 'postiz',
|
||||
name: 'postiz',
|
||||
description: 'Agent that helps manage and schedule social media posts for users',
|
||||
instructions: ({ runtimeContext }) => {
|
||||
const ui: string = runtimeContext.get('ui' as never);
|
||||
instructions: ({ requestContext }) => {
|
||||
const ui: string = requestContext.get('ui' as never);
|
||||
return `
|
||||
Global information:
|
||||
- Date (UTC): ${dayjs().format('YYYY-MM-DD HH:mm:ss')}
|
||||
|
|
@ -90,9 +91,7 @@ export class LoadToolsService {
|
|||
memory: new Memory({
|
||||
storage: pStore,
|
||||
options: {
|
||||
threads: {
|
||||
generateTitle: true,
|
||||
},
|
||||
generateTitle: true,
|
||||
workingMemory: {
|
||||
enabled: true,
|
||||
schema: AgentState,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { PostgresStore, PgVector } from '@mastra/pg';
|
||||
import { PostgresStore } from '@mastra/pg';
|
||||
|
||||
export const pStore = new PostgresStore({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
id: 'postiz-store',
|
||||
connectionString: process.env.DATABASE_URL!,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { randomUUID } from 'crypto';
|
|||
import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service';
|
||||
import { OAuthService } from '@gitroom/nestjs-libraries/database/prisma/oauth/oauth.service';
|
||||
import { runWithContext } from './async.storage';
|
||||
|
||||
export const startMcp = async (app: INestApplication) => {
|
||||
const mastraService = app.get(MastraService, { strict: false });
|
||||
const organizationService = app.get(OrganizationService, { strict: false });
|
||||
|
|
@ -22,7 +23,7 @@ export const startMcp = async (app: INestApplication) => {
|
|||
|
||||
const mastra = await mastraService.mastra();
|
||||
const agent = mastra.getAgent('postiz');
|
||||
const tools = await agent.getTools();
|
||||
const tools = await agent.listTools();
|
||||
|
||||
const serverConfig = {
|
||||
name: 'Postiz MCP',
|
||||
|
|
@ -76,6 +77,7 @@ export const startMcp = async (app: INestApplication) => {
|
|||
sessionIdGenerator: () => {
|
||||
return randomUUID();
|
||||
},
|
||||
enableJsonResponse: true,
|
||||
},
|
||||
req,
|
||||
res,
|
||||
|
|
@ -119,6 +121,7 @@ export const startMcp = async (app: INestApplication) => {
|
|||
sessionIdGenerator: () => {
|
||||
return randomUUID();
|
||||
},
|
||||
enableJsonResponse: true,
|
||||
},
|
||||
req,
|
||||
res,
|
||||
|
|
|
|||
|
|
@ -27,13 +27,11 @@ export class GenerateImageTool implements AgentToolInterface {
|
|||
id: z.string(),
|
||||
path: z.string(),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
// @ts-ignore
|
||||
const org = JSON.parse(runtimeContext.get('organization') as string);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const org = JSON.parse((context?.requestContext as any)?.get('organization') as string);
|
||||
const image = await this._mediaService.generateImage(
|
||||
context.prompt,
|
||||
inputData.prompt,
|
||||
org
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
AgentToolInterface,
|
||||
ToolReturn,
|
||||
} from '@gitroom/nestjs-libraries/chat/agent.tool.interface';
|
||||
import { createTool } from '@mastra/core/tools';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
|
@ -33,9 +32,8 @@ export class GenerateVideoOptionsTool implements AgentToolInterface {
|
|||
})
|
||||
),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const videos = this._videoManagerService.getAllVideos();
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
|
|
|
|||
|
|
@ -48,20 +48,18 @@ export class GenerateVideoTool implements AgentToolInterface {
|
|||
outputSchema: z.object({
|
||||
url: z.string(),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
// @ts-ignore
|
||||
const org = JSON.parse(runtimeContext.get('organization') as string);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const org = JSON.parse((context?.requestContext as any)?.get('organization') as string);
|
||||
const value = await this._mediaService.generateVideo(org, {
|
||||
type: context.identifier,
|
||||
output: context.output,
|
||||
customParams: context.customParams.reduce(
|
||||
(all, current) => ({
|
||||
type: inputData.identifier,
|
||||
output: inputData.output,
|
||||
customParams: inputData.customParams.reduce(
|
||||
(all: Record<string, any>, current: { key: string; value: any }) => ({
|
||||
...all,
|
||||
[current.key]: current.value,
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, any>
|
||||
),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import {
|
||||
AgentToolInterface,
|
||||
ToolReturn,
|
||||
} from '@gitroom/nestjs-libraries/chat/agent.tool.interface';
|
||||
import { createTool } from '@mastra/core/tools';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service';
|
||||
import z from 'zod';
|
||||
import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context';
|
||||
import { getAuth } from '@gitroom/nestjs-libraries/chat/async.storage';
|
||||
|
||||
@Injectable()
|
||||
export class IntegrationListTool implements AgentToolInterface {
|
||||
|
|
@ -28,14 +26,10 @@ export class IntegrationListTool implements AgentToolInterface {
|
|||
})
|
||||
),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
console.log(getAuth());
|
||||
console.log(options);
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const organizationId = JSON.parse(
|
||||
// @ts-ignore
|
||||
runtimeContext.get('organization') as string
|
||||
(context?.requestContext as any)?.get('organization') as string
|
||||
).id;
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -114,17 +114,15 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
)
|
||||
.or(z.object({ errors: z.string() })),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const organizationId = JSON.parse(
|
||||
// @ts-ignore
|
||||
runtimeContext.get('organization') as string
|
||||
(context?.requestContext as any)?.get('organization') as string
|
||||
).id;
|
||||
const finalOutput = [];
|
||||
|
||||
const integrations = {} as Record<string, Integration>;
|
||||
for (const platform of context.socialPost) {
|
||||
for (const platform of inputData.socialPost) {
|
||||
integrations[platform.integrationId] =
|
||||
await this._integrationService.getIntegrationById(
|
||||
organizationId,
|
||||
|
|
@ -142,7 +140,7 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
const obj = Object.assign(
|
||||
newDTO,
|
||||
platform.settings.reduce(
|
||||
(acc, s) => ({
|
||||
(acc: AllProvidersSettings, s: { key: string; value: any }) => ({
|
||||
...acc,
|
||||
[s.key]: s.value,
|
||||
}),
|
||||
|
|
@ -180,7 +178,7 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
}
|
||||
}
|
||||
|
||||
for (const post of context.socialPost) {
|
||||
for (const post of inputData.socialPost) {
|
||||
const integration = integrations[post.integrationId];
|
||||
|
||||
if (!integration) {
|
||||
|
|
@ -197,7 +195,7 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
integration,
|
||||
group: makeId(10),
|
||||
settings: post.settings.reduce(
|
||||
(acc, s) => ({
|
||||
(acc: AllProvidersSettings, s: { key: string; value: any }) => ({
|
||||
...acc,
|
||||
[s.key]: s.value,
|
||||
}),
|
||||
|
|
@ -205,11 +203,11 @@ If the tools return errors, you would need to rerun it with the right parameters
|
|||
__type: integration.providerIdentifier,
|
||||
} as AllProvidersSettings
|
||||
),
|
||||
value: post.postsAndComments.map((p) => ({
|
||||
value: post.postsAndComments.map((p: any) => ({
|
||||
content: p.content,
|
||||
id: makeId(10),
|
||||
delay: 0,
|
||||
image: p.attachments.map((p) => ({
|
||||
image: p.attachments.map((p: any) => ({
|
||||
id: makeId(10),
|
||||
path: p,
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -43,19 +43,17 @@ export class IntegrationTriggerTool implements AgentToolInterface {
|
|||
outputSchema: z.object({
|
||||
output: z.array(z.record(z.string(), z.any())),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
console.log('triggerTool', context);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
console.log('triggerTool', inputData);
|
||||
const organizationId = JSON.parse(
|
||||
// @ts-ignore
|
||||
runtimeContext.get('organization') as string
|
||||
(context?.requestContext as any)?.get('organization') as string
|
||||
).id;
|
||||
|
||||
const getIntegration =
|
||||
await this._integrationService.getIntegrationById(
|
||||
organizationId,
|
||||
context.integrationId
|
||||
inputData.integrationId
|
||||
);
|
||||
|
||||
if (!getIntegration) {
|
||||
|
|
@ -78,10 +76,10 @@ export class IntegrationTriggerTool implements AgentToolInterface {
|
|||
if (
|
||||
// @ts-ignore
|
||||
!tools[integrationProvider.identifier].some(
|
||||
(p) => p.methodName === context.methodName
|
||||
(p) => p.methodName === inputData.methodName
|
||||
) ||
|
||||
// @ts-ignore
|
||||
!integrationProvider[context.methodName]
|
||||
!integrationProvider[inputData.methodName]
|
||||
) {
|
||||
return { output: 'tool not found' };
|
||||
}
|
||||
|
|
@ -89,14 +87,14 @@ export class IntegrationTriggerTool implements AgentToolInterface {
|
|||
while (true) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const load = await integrationProvider[context.methodName](
|
||||
const load = await integrationProvider[inputData.methodName](
|
||||
getIntegration.token,
|
||||
context.dataSchema.reduce(
|
||||
(all, current) => ({
|
||||
inputData.dataSchema.reduce(
|
||||
(all: Record<string, string>, current: { key: string; value: string }) => ({
|
||||
...all,
|
||||
[current.key]: current.value,
|
||||
}),
|
||||
{}
|
||||
{} as Record<string, string>
|
||||
),
|
||||
getIntegration.internalId,
|
||||
getIntegration
|
||||
|
|
|
|||
|
|
@ -72,11 +72,10 @@ export class IntegrationValidationTool implements AgentToolInterface {
|
|||
),
|
||||
}),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const integration = socialIntegrationList.find(
|
||||
(p) => p.identifier === context.platform
|
||||
(p) => p.identifier === inputData.platform
|
||||
)!;
|
||||
|
||||
if (!integration) {
|
||||
|
|
@ -85,7 +84,7 @@ export class IntegrationValidationTool implements AgentToolInterface {
|
|||
};
|
||||
}
|
||||
|
||||
const maxLength = integration.maxLength(context.isPremium);
|
||||
const maxLength = integration.maxLength(inputData.isPremium);
|
||||
const schemas = !integration.dto
|
||||
? false
|
||||
: getValidationSchemas()[integration.dto.name];
|
||||
|
|
|
|||
|
|
@ -22,14 +22,13 @@ export class VideoFunctionTool implements AgentToolInterface {
|
|||
identifier: z.string(),
|
||||
functionName: z.string(),
|
||||
}),
|
||||
execute: async (args, options) => {
|
||||
const { context, runtimeContext } = args;
|
||||
checkAuth(args, options);
|
||||
execute: async (inputData, context) => {
|
||||
checkAuth(inputData, context);
|
||||
const videos = this._videoManagerService.getAllVideos();
|
||||
const findVideo = videos.find(
|
||||
(p) =>
|
||||
p.identifier === context.identifier &&
|
||||
p.tools.some((p) => p.functionName === context.functionName)
|
||||
p.identifier === inputData.identifier &&
|
||||
p.tools.some((p) => p.functionName === inputData.functionName)
|
||||
);
|
||||
|
||||
if (!findVideo) {
|
||||
|
|
@ -39,7 +38,7 @@ export class VideoFunctionTool implements AgentToolInterface {
|
|||
const func = await this._moduleRef
|
||||
// @ts-ignore
|
||||
.get(findVideo.target, { strict: false })
|
||||
[context.functionName]();
|
||||
[inputData.functionName]();
|
||||
return func;
|
||||
},
|
||||
});
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -42,7 +42,7 @@
|
|||
"test": "jest --coverage --detectOpenHandles --reporters=default --reporters=jest-junit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-ui/mastra": "0.2.0",
|
||||
"@ag-ui/mastra": "^1.0.1",
|
||||
"@ai-sdk/openai": "^2.0.52",
|
||||
"@atproto/api": "^0.15.15",
|
||||
"@aws-sdk/client-s3": "^3.787.0",
|
||||
|
|
@ -63,9 +63,10 @@
|
|||
"@mantine/dates": "^5.10.5",
|
||||
"@mantine/hooks": "^5.10.5",
|
||||
"@mantine/modals": "^5.10.5",
|
||||
"@mastra/core": "^0.20.2",
|
||||
"@mastra/memory": "^0.15.6",
|
||||
"@mastra/pg": "^0.17.2",
|
||||
"@mastra/core": "^1.21.0",
|
||||
"@mastra/mcp": "^1.4.1",
|
||||
"@mastra/memory": "^1.13.0",
|
||||
"@mastra/pg": "^1.8.5",
|
||||
"@modelcontextprotocol/sdk": "^1.22.0",
|
||||
"@nest-lab/throttler-storage-redis": "^1.2.0",
|
||||
"@nestjs/cli": "10.0.2",
|
||||
|
|
@ -178,7 +179,7 @@
|
|||
"json-to-graphql-query": "^2.2.5",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mastra": "^0.13.2",
|
||||
"mastra": "^1.3.19",
|
||||
"md5": "^2.3.0",
|
||||
"mime": "^3.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
|
|
|
|||
1928
pnpm-lock.yaml
generated
1928
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue