Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/core/execution/src/lib/workers/job-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ export type UserInteractionJobDataWithoutWatchingInformation = z.infer<typeof Us
export enum AgentRunSource {
CHAT = 'CHAT',
FLOW_STEP = 'FLOW_STEP',
AGENT = 'AGENT',
}

export const AgentPromptOverride = z.object({
Expand Down
2 changes: 1 addition & 1 deletion packages/core/shared/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@activepieces/shared",
"version": "0.136.0",
"version": "0.137.0",
"type": "commonjs",
"sideEffects": false,
"main": "./dist/src/index.js",
Expand Down
7 changes: 6 additions & 1 deletion packages/core/shared/src/lib/ee/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ const Agent = z.object({
published: Nullable(AgentConfig),
})

const AgentSummary = Agent.omit({ draft: true, published: true })
const AgentSummary = Agent.omit({ draft: true, published: true }).extend({
toolCount: z.number(),
toolPieceNames: z.array(z.string()),
projectDisplayName: z.string(),
projectIsPrivate: z.boolean(),
})

const CreateAgentRequest = z.object({
projectId: ApId,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/shared/src/lib/ee/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export const AgentConversation = z.object({
platformId: z.string(),
projectId: Nullable(z.string()),
userId: z.string(),
agentId: Nullable(z.string()),
source: z.enum(AgentRunSource),
title: Nullable(z.string()),
modelName: Nullable(z.string()),
Expand All @@ -217,6 +218,7 @@ export type AgentConversation = z.infer<typeof AgentConversation>
export const CreateAgentConversationRequest = z.object({
title: z.optional(Nullable(z.string())),
modelName: z.optional(Nullable(z.string())),
agentId: z.optional(z.string()),
})
export type CreateAgentConversationRequest = z.infer<typeof CreateAgentConversationRequest>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { QueryRunner } from 'typeorm'
import { Migration } from '../../migration'

export class AddAgentIdToAgentConversation1826000000000 implements Migration {
name = 'AddAgentIdToAgentConversation1826000000000'
breaking = false
release = '0.88.1'
transaction = true

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "agent_conversation"
ADD "agentId" character varying(21)
`)
await queryRunner.query(`
ALTER TABLE "agent_conversation"
ADD CONSTRAINT "fk_agent_conversation_agent_id"
FOREIGN KEY ("agentId") REFERENCES "agent"("id") ON DELETE CASCADE
`)
await queryRunner.query(`
CREATE INDEX "idx_agent_conversation_agent_user_created_id"
ON "agent_conversation" ("agentId", "userId", "created", "id")
WHERE "agentId" IS NOT NULL
`)
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
DROP INDEX "idx_agent_conversation_agent_user_created_id"
`)
await queryRunner.query(`
ALTER TABLE "agent_conversation"
DROP CONSTRAINT "fk_agent_conversation_agent_id"
`)
await queryRunner.query(`
ALTER TABLE "agent_conversation" DROP COLUMN "agentId"
`)
}
}
2 changes: 2 additions & 0 deletions packages/server/api/src/app/database/postgres-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ import { RenameChatTablesToAgent1822000000000 } from './migration/postgres/18220
import { AddRenamedChatTableCompatViews1823000000000 } from './migration/postgres/1823000000000-AddRenamedChatTableCompatViews'
import { AddAttemptsToOtp1824000000000 } from './migration/postgres/1824000000000-AddAttemptsToOtp'
import { AddAgentTable1825000000000 } from './migration/postgres/1825000000000-AddAgentTable'
import { AddAgentIdToAgentConversation1826000000000 } from './migration/postgres/1826000000000-AddAgentIdToAgentConversation'

const getSslConfig = (): boolean | TlsOptions => {
const useSsl = system.get(AppSystemProp.POSTGRES_USE_SSL)
Expand Down Expand Up @@ -847,6 +848,7 @@ export const getMigrations = (): (new () => Migration)[] => {
AddRenamedChatTableCompatViews1823000000000,
AddAttemptsToOtp1824000000000,
AddAgentTable1825000000000,
AddAgentIdToAgentConversation1826000000000,
]
return migrations
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ActivepiecesError, apId, ErrorCode, isNil, spreadIfDefined, tryCatch } from '@activepieces/core-utils'
import { AgentConversationStatus, CreateAgentConversationRequest, ImportAgentMemoryRequest, InstructAgentMemoryRequest, LATEST_JOB_DATA_SCHEMA_VERSION, PrincipalType, SendAgentMessageRequest, SERVICE_KEY_SECURITY_OPENAPI, SetAgentMessageFeedbackRequest, UpdateAgentConversationRequest, UpdateAgentMemoryRequest, WorkerJobType } from '@activepieces/shared'
import { AgentConversationStatus, AgentRunSource, CreateAgentConversationRequest, ImportAgentMemoryRequest, InstructAgentMemoryRequest, LATEST_JOB_DATA_SCHEMA_VERSION, PrincipalType, SendAgentMessageRequest, SERVICE_KEY_SECURITY_OPENAPI, SetAgentMessageFeedbackRequest, UpdateAgentConversationRequest, UpdateAgentMemoryRequest, WorkerJobType } from '@activepieces/shared'
import { FastifyBaseLogger } from 'fastify'
import { FastifyPluginAsyncZod } from 'fastify-type-provider-zod'
import { StatusCodes } from 'http-status-codes'
Expand All @@ -11,6 +11,7 @@ import { agentApprovalGate } from './agent-approval-gate'
import { agentConversationService } from './agent-conversation-service'
import { agentHelpers } from './agent-helpers'
import { agentMemoryAi } from './agent-memory-ai'
import { agentService } from './agent-service'
import { chatAnalyticsTelemetry } from './chat-analytics-sync'
import { chatPlanGrant } from './chat-plan-grant'
import { chatRolloutService } from './chat-rollout-service'
Expand All @@ -35,6 +36,7 @@ export const agentConversationController: FastifyPluginAsyncZod = async (app) =>
userId: request.principal.id,
cursor: request.query.cursor,
limit: request.query.limit ?? 20,
...spreadIfDefined('agentId', request.query.agentId),
})
})

Expand Down Expand Up @@ -149,7 +151,20 @@ export const agentConversationController: FastifyPluginAsyncZod = async (app) =>
await agentApprovalGate.clearPendingGate({ conversationId })
}

await agentHelpers.assertRunProviderConfigured({ platformId, log })
const agent = isNil(conversation.agentId)
? null
: await agentService(log).getOneOrThrowByPlatform({ id: conversation.agentId, platformId, userId })
const agentConfig = agent?.published ?? agent?.draft ?? null
// resolveRunProvider and the assertion below both fall through to the platform's chat
// provider when no provider is named. An agent answers on its own model or it does not run.
if (!isNil(agent) && (isNil(agentConfig?.provider) || isNil(agentConfig?.modelName))) {
throw new ActivepiecesError({
code: ErrorCode.VALIDATION,
params: { message: 'Pick a model for this agent before talking to it' },
})
}

await agentHelpers.assertRunProviderConfigured({ platformId, log, ...spreadIfDefined('provider', agentConfig?.provider ?? undefined) })
await assertCreditsAndAppSumoNotExceeded({ platformId, log })

await jobQueue(runLog).add({
Expand All @@ -164,8 +179,16 @@ export const agentConversationController: FastifyPluginAsyncZod = async (app) =>
platformId,
userId,
userMessage: content,
modelName: conversation.modelName ?? null,
modelName: isNil(agent) ? conversation.modelName ?? null : agentConfig?.modelName ?? null,
files,
...spreadIfDefined('source', isNil(agent) ? undefined : AgentRunSource.AGENT),
...(isNil(agentConfig) ? {} : {
tools: agentConfig.tools,
structuredOutput: agentConfig.structuredOutput,
maxSteps: agentConfig.maxSteps,
...spreadIfDefined('provider', agentConfig.provider ?? undefined),
promptOverride: { system: agentConfig.instructions },
}),
},
})
runLog.info({ job: { type: WorkerJobType.EXECUTE_AGENT_RUN } }, '[agentConversationController] Enqueued chat agent job')
Expand Down Expand Up @@ -319,6 +342,7 @@ const ListConversationsRoute = {
querystring: z.object({
cursor: z.string().optional(),
limit: z.coerce.number().int().min(1).max(100).default(20).optional(),
agentId: z.string().optional(),
}),
},
}
Expand Down
23 changes: 21 additions & 2 deletions packages/server/api/src/app/ee/agent/agent-conversation-entity.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { AgentConversation, AgentConversationStatus, AgentRunSource, Platform, Project, User } from '@activepieces/shared'
import { Agent, AgentConversation, AgentConversationStatus, AgentRunSource, Platform, Project, User } from '@activepieces/shared'
import { EntitySchema } from 'typeorm'
import { ApIdSchema, BaseColumnSchemaPart } from '../../database/database-common'

type AgentConversationWithRelations = AgentConversation & {
export type AgentConversationWithRelations = AgentConversation & {
platform: Platform
project: Project
user: User
agent: Agent
}

export const AgentConversationEntity = new EntitySchema<AgentConversationWithRelations>({
Expand All @@ -24,6 +25,10 @@ export const AgentConversationEntity = new EntitySchema<AgentConversationWithRel
...ApIdSchema,
nullable: false,
},
agentId: {
...ApIdSchema,
nullable: true,
},
source: {
type: String,
nullable: false,
Expand Down Expand Up @@ -74,6 +79,11 @@ export const AgentConversationEntity = new EntitySchema<AgentConversationWithRel
columns: ['created', 'projectId'],
where: `source = '${AgentRunSource.FLOW_STEP}'`,
},
{
name: 'idx_agent_conversation_agent_user_created_id',
columns: ['agentId', 'userId', 'created', 'id'],
where: '"agentId" IS NOT NULL',
},
{
name: 'idx_agent_conversation_streaming_updated',
columns: ['updated'],
Expand Down Expand Up @@ -101,6 +111,15 @@ export const AgentConversationEntity = new EntitySchema<AgentConversationWithRel
foreignKeyConstraintName: 'fk_agent_conversation_project_id',
},
},
agent: {
type: 'many-to-one',
target: 'agent',
onDelete: 'CASCADE',
joinColumn: {
name: 'agentId',
foreignKeyConstraintName: 'fk_agent_conversation_agent_id',
},
},
user: {
type: 'many-to-one',
target: 'user',
Expand Down
23 changes: 18 additions & 5 deletions packages/server/api/src/app/ee/agent/agent-conversation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import { Order } from '../../helper/pagination/paginator'
import { agentApprovalGate } from './agent-approval-gate'
import { AgentConversationEntity } from './agent-conversation-entity'
import { agentHelpers, EVAL_CONVERSATION_ID_PREFIX, isEvalConversationId } from './agent-helpers'
import { agentService } from './agent-service'
import { agentHistory } from './history/agent-history'

export const agentConversationService = (log: FastifyBaseLogger) => ({
async createConversation({ platformId, userId, request, id }: CreateConversationParams): Promise<AgentConversation> {
const agent = isNil(request.agentId)
? null
: await agentService(log).getOneOrThrowByPlatform({ id: request.agentId, platformId, userId })
const conversation = await agentHelpers.conversationRepo().save({
id: id ?? apId(),
platformId,
projectId: null,
projectId: agent?.projectId ?? null,
userId,
source: AgentRunSource.CHAT,
agentId: agent?.id ?? null,
source: isNil(agent) ? AgentRunSource.CHAT : AgentRunSource.AGENT,
title: request.title ?? null,
modelName: request.modelName ?? null,
messages: [],
Expand All @@ -26,7 +31,7 @@ export const agentConversationService = (log: FastifyBaseLogger) => ({
return conversation
},

async listConversations({ platformId, userId, cursor, limit }: ListConversationsParams): Promise<SeekPage<AgentConversation>> {
async listConversations({ platformId, userId, cursor, limit, agentId }: ListConversationsParams): Promise<SeekPage<AgentConversation>> {
const decodedCursor = paginationHelper.decodeCursor(cursor)
const paginator = buildPaginator({
entity: AgentConversationEntity,
Expand Down Expand Up @@ -57,7 +62,14 @@ export const agentConversationService = (log: FastifyBaseLogger) => ({
.where({ platformId, userId })
// Eval conversations are owned by the platform owner; keep them out of the regular list.
.andWhere('agent_conversation.id NOT LIKE :evalPrefix', { evalPrefix: `${EVAL_CONVERSATION_ID_PREFIX}%` })
.andWhere('agent_conversation.source = :chatSource', { chatSource: AgentRunSource.CHAT })
if (isNil(agentId)) {
queryBuilder.andWhere('agent_conversation.source = :chatSource', { chatSource: AgentRunSource.CHAT })
}
else {
queryBuilder
.andWhere('agent_conversation.source = :agentSource', { agentSource: AgentRunSource.AGENT })
.andWhere('agent_conversation."agentId" = :agentId', { agentId })
}

const { data, cursor: paginationCursor } = await paginator.paginate(queryBuilder)
return paginationHelper.createPage(data, paginationCursor)
Expand All @@ -70,7 +82,7 @@ export const agentConversationService = (log: FastifyBaseLogger) => ({
throw new ActivepiecesError({ code: ErrorCode.ENTITY_NOT_FOUND, params: { entityId: id, entityType: 'AgentConversation' } })
}
const conversation = await agentHelpers.getConversationOrThrow({ id, platformId, userId, log })
if (conversation.source !== AgentRunSource.CHAT) {
if (![AgentRunSource.CHAT, AgentRunSource.AGENT].includes(conversation.source)) {
throw new ActivepiecesError({ code: ErrorCode.ENTITY_NOT_FOUND, params: { entityId: id, entityType: 'AgentConversation' } })
}
return conversation
Expand Down Expand Up @@ -151,6 +163,7 @@ type ListConversationsParams = {
userId: string
cursor?: string
limit: number
agentId?: string
}

type ConversationIdentifier = {
Expand Down
Loading
Loading