Skip to content

Commit 50fb0f8

Browse files
authored
Merge pull request #1027 from gitroomhq/feat/agent
Postiz Agent
2 parents 79de287 + d079c1b commit 50fb0f8

File tree

107 files changed

+7491
-3858
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+7491
-3858
lines changed

apps/backend/src/api/api.module.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ import { Nowpayments } from '@gitroom/nestjs-libraries/crypto/nowpayments';
3131
import { WebhookController } from '@gitroom/backend/api/routes/webhooks.controller';
3232
import { SignatureController } from '@gitroom/backend/api/routes/signature.controller';
3333
import { AutopostController } from '@gitroom/backend/api/routes/autopost.controller';
34-
import { McpService } from '@gitroom/nestjs-libraries/mcp/mcp.service';
35-
import { McpController } from '@gitroom/backend/api/routes/mcp.controller';
3634
import { SetsController } from '@gitroom/backend/api/routes/sets.controller';
3735
import { ThirdPartyController } from '@gitroom/backend/api/routes/third-party.controller';
3836
import { MonitorController } from '@gitroom/backend/api/routes/monitor.controller';
@@ -63,7 +61,6 @@ const authenticatedController = [
6361
StripeController,
6462
AuthController,
6563
PublicController,
66-
McpController,
6764
MonitorController,
6865
...authenticatedController,
6966
],
@@ -80,7 +77,6 @@ const authenticatedController = [
8077
TrackService,
8178
ShortLinkService,
8279
Nowpayments,
83-
McpService,
8480
],
8581
get exports() {
8682
return [...this.imports, ...this.providers];
Lines changed: 122 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,41 @@
1-
import { Logger, Controller, Get, Post, Req, Res, Query } from '@nestjs/common';
1+
import {
2+
Logger,
3+
Controller,
4+
Get,
5+
Post,
6+
Req,
7+
Res,
8+
Query,
9+
Param,
10+
} from '@nestjs/common';
211
import {
312
CopilotRuntime,
413
OpenAIAdapter,
5-
copilotRuntimeNestEndpoint,
14+
copilotRuntimeNodeHttpEndpoint,
15+
copilotRuntimeNextJSAppRouterEndpoint,
616
} from '@copilotkit/runtime';
717
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
818
import { Organization } from '@prisma/client';
919
import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service';
20+
import { MastraAgent } from '@ag-ui/mastra';
21+
import { MastraService } from '@gitroom/nestjs-libraries/chat/mastra.service';
22+
import { Request, Response } from 'express';
23+
import { RuntimeContext } from '@mastra/core/di';
24+
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
25+
import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/permissions/permission.exception.class';
26+
27+
export type ChannelsContext = {
28+
integrations: string;
29+
organization: string;
30+
ui: string;
31+
};
1032

1133
@Controller('/copilot')
1234
export class CopilotController {
13-
constructor(private _subscriptionService: SubscriptionService) {}
35+
constructor(
36+
private _subscriptionService: SubscriptionService,
37+
private _mastraService: MastraService
38+
) {}
1439
@Post('/chat')
1540
chat(@Req() req: Request, @Res() res: Response) {
1641
if (
@@ -21,28 +46,112 @@ export class CopilotController {
2146
return;
2247
}
2348

24-
const copilotRuntimeHandler = copilotRuntimeNestEndpoint({
49+
const copilotRuntimeHandler = copilotRuntimeNodeHttpEndpoint({
2550
endpoint: '/copilot/chat',
2651
runtime: new CopilotRuntime(),
2752
serviceAdapter: new OpenAIAdapter({
28-
model:
29-
// @ts-ignore
30-
req?.body?.variables?.data?.metadata?.requestType ===
31-
'TextareaCompletion'
32-
? 'gpt-4o-mini'
33-
: 'gpt-4.1',
53+
model: 'gpt-4.1',
3454
}),
3555
});
3656

37-
// @ts-ignore
3857
return copilotRuntimeHandler(req, res);
3958
}
4059

60+
@Post('/agent')
61+
@CheckPolicies([AuthorizationActions.Create, Sections.AI])
62+
async agent(
63+
@Req() req: Request,
64+
@Res() res: Response,
65+
@GetOrgFromRequest() organization: Organization
66+
) {
67+
if (
68+
process.env.OPENAI_API_KEY === undefined ||
69+
process.env.OPENAI_API_KEY === ''
70+
) {
71+
Logger.warn('OpenAI API key not set, chat functionality will not work');
72+
return;
73+
}
74+
const mastra = await this._mastraService.mastra();
75+
const runtimeContext = new RuntimeContext<ChannelsContext>();
76+
runtimeContext.set(
77+
'integrations',
78+
req?.body?.variables?.properties?.integrations || []
79+
);
80+
81+
runtimeContext.set('organization', JSON.stringify(organization));
82+
runtimeContext.set('ui', 'true');
83+
84+
const agents = MastraAgent.getLocalAgents({
85+
resourceId: organization.id,
86+
mastra,
87+
// @ts-ignore
88+
runtimeContext,
89+
});
90+
91+
const runtime = new CopilotRuntime({
92+
agents,
93+
});
94+
95+
const copilotRuntimeHandler = copilotRuntimeNextJSAppRouterEndpoint({
96+
endpoint: '/copilot/agent',
97+
runtime,
98+
// properties: req.body.variables.properties,
99+
serviceAdapter: new OpenAIAdapter({
100+
model: 'gpt-4.1',
101+
}),
102+
});
103+
104+
return copilotRuntimeHandler.handleRequest(req, res);
105+
}
106+
41107
@Get('/credits')
42108
calculateCredits(
43109
@GetOrgFromRequest() organization: Organization,
44-
@Query('type') type: 'ai_images' | 'ai_videos',
110+
@Query('type') type: 'ai_images' | 'ai_videos'
45111
) {
46-
return this._subscriptionService.checkCredits(organization, type || 'ai_images');
112+
return this._subscriptionService.checkCredits(
113+
organization,
114+
type || 'ai_images'
115+
);
116+
}
117+
118+
@Get('/:thread/list')
119+
@CheckPolicies([AuthorizationActions.Create, Sections.AI])
120+
async getMessagesList(
121+
@GetOrgFromRequest() organization: Organization,
122+
@Param('thread') threadId: string
123+
): Promise<any> {
124+
const mastra = await this._mastraService.mastra();
125+
const memory = await mastra.getAgent('postiz').getMemory();
126+
try {
127+
return await memory.query({
128+
resourceId: organization.id,
129+
threadId,
130+
});
131+
} catch (err) {
132+
return { messages: [] };
133+
}
134+
}
135+
136+
@Get('/list')
137+
@CheckPolicies([AuthorizationActions.Create, Sections.AI])
138+
async getList(@GetOrgFromRequest() organization: Organization) {
139+
const mastra = await this._mastraService.mastra();
140+
// @ts-ignore
141+
const memory = await mastra.getAgent('postiz').getMemory();
142+
const list = await memory.getThreadsByResourceIdPaginated({
143+
resourceId: organization.id,
144+
perPage: 100000,
145+
page: 0,
146+
orderBy: 'createdAt',
147+
sortDirection: 'DESC',
148+
});
149+
150+
return {
151+
threads: list.threads.map((p) => ({
152+
id: p.id,
153+
title: p.title,
154+
})),
155+
};
47156
}
48157
}

apps/backend/src/api/routes/integrations.controller.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integ
1616
import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service';
1717
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
1818
import { Organization, User } from '@prisma/client';
19-
import { ApiKeyDto } from '@gitroom/nestjs-libraries/dtos/integrations/api.key.dto';
2019
import { IntegrationFunctionDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.function.dto';
2120
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
2221
import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing';

apps/backend/src/api/routes/mcp.controller.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

apps/backend/src/app.module.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import { PublicApiModule } from '@gitroom/backend/public-api/public.api.module';
88
import { ThrottlerBehindProxyGuard } from '@gitroom/nestjs-libraries/throttler/throttler.provider';
99
import { ThrottlerModule } from '@nestjs/throttler';
1010
import { AgentModule } from '@gitroom/nestjs-libraries/agent/agent.module';
11-
import { McpModule } from '@gitroom/backend/mcp/mcp.module';
1211
import { ThirdPartyModule } from '@gitroom/nestjs-libraries/3rdparties/thirdparty.module';
1312
import { VideoModule } from '@gitroom/nestjs-libraries/videos/video.module';
14-
import { SentryModule } from "@sentry/nestjs/setup";
13+
import { SentryModule } from '@sentry/nestjs/setup';
1514
import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception';
15+
import { ChatModule } from '@gitroom/nestjs-libraries/chat/chat.module';
1616

1717
@Global()
1818
@Module({
@@ -23,9 +23,9 @@ import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception';
2323
ApiModule,
2424
PublicApiModule,
2525
AgentModule,
26-
McpModule,
2726
ThirdPartyModule,
2827
VideoModule,
28+
ChatModule,
2929
ThrottlerModule.forRoot([
3030
{
3131
ttl: 3600000,
@@ -43,16 +43,16 @@ import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception';
4343
{
4444
provide: APP_GUARD,
4545
useClass: PoliciesGuard,
46-
}
46+
},
4747
],
4848
exports: [
4949
BullMqModule,
5050
DatabaseModule,
5151
ApiModule,
5252
PublicApiModule,
5353
AgentModule,
54-
McpModule,
5554
ThrottlerModule,
55+
ChatModule,
5656
],
5757
})
5858
export class AppModule {}

apps/backend/src/main.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { loadSwagger } from '@gitroom/helpers/swagger/load.swagger';
2+
import { json } from 'express';
23

34
process.env.TZ = 'UTC';
45

@@ -13,12 +14,14 @@ initializeSentry('backend', true);
1314
import { SubscriptionExceptionFilter } from '@gitroom/backend/services/auth/permissions/subscription.exception';
1415
import { HttpExceptionFilter } from '@gitroom/nestjs-libraries/services/exception.filter';
1516
import { ConfigurationChecker } from '@gitroom/helpers/configuration/configuration.checker';
17+
import { startMcp } from '@gitroom/nestjs-libraries/chat/start.mcp';
1618

1719
async function bootstrap() {
1820
const app = await NestFactory.create(AppModule, {
1921
rawBody: true,
2022
cors: {
2123
...(!process.env.NOT_SECURED ? { credentials: true } : {}),
24+
allowedHeaders: ['Content-Type', 'Authorization'],
2225
exposedHeaders: [
2326
'reload',
2427
'onboarding',
@@ -27,17 +30,24 @@ async function bootstrap() {
2730
],
2831
origin: [
2932
process.env.FRONTEND_URL,
33+
'http://localhost:6274',
3034
...(process.env.MAIN_URL ? [process.env.MAIN_URL] : []),
3135
],
3236
},
3337
});
3438

39+
await startMcp(app);
40+
3541
app.useGlobalPipes(
3642
new ValidationPipe({
3743
transform: true,
3844
})
3945
);
4046

47+
app.use('/copilot', (req: any, res: any, next: any) => {
48+
json({ limit: '50mb' })(req, res, next);
49+
});
50+
4151
app.use(cookieParser());
4252
app.useGlobalFilters(new SubscriptionExceptionFilter());
4353
app.useGlobalFilters(new HttpExceptionFilter());

0 commit comments

Comments
 (0)