diff --git a/front/admin/db.ts b/front/admin/db.ts index 29890f3a3d0b..477b50f3b030 100644 --- a/front/admin/db.ts +++ b/front/admin/db.ts @@ -56,11 +56,10 @@ async function main() { await ExtractedEvent.sync({ alter: true }); await DocumentTrackerChangeSuggestion.sync({ alter: true }); - await AgentConfiguration.sync({ alter: true }); await AgentGenerationConfiguration.sync({ alter: true }); - await AgentRetrievalConfiguration.sync({ alter: true }); await AgentDataSourceConfiguration.sync({ alter: true }); + await AgentConfiguration.sync({ alter: true }); await AgentRetrievalAction.sync({ alter: true }); await RetrievalDocument.sync({ alter: true }); await RetrievalDocumentChunk.sync({ alter: true }); diff --git a/front/lib/api/assistant/actions/retrieval.ts b/front/lib/api/assistant/actions/retrieval.ts index e7a77e7ecc45..0c65fa0f468b 100644 --- a/front/lib/api/assistant/actions/retrieval.ts +++ b/front/lib/api/assistant/actions/retrieval.ts @@ -17,17 +17,17 @@ import { Err, Ok, Result } from "@app/lib/result"; import { new_id } from "@app/lib/utils"; import logger from "@app/logger/logger"; import { - DataSourceConfiguration, - isRetrievalConfiguration, RetrievalActionType, - RetrievalConfigurationType, RetrievalDocumentType, TimeFrame, } from "@app/types/assistant/actions/retrieval"; import { - AgentActionSpecification, - AgentConfigurationType, -} from "@app/types/assistant/agent"; + AgentDataSourceConfigurationType, + isRetrievalConfiguration, + RetrievalConfigurationType, +} from "@app/types/assistant/actions/retrieval"; +import { AgentActionSpecification } from "@app/types/assistant/agent"; +import { AgentConfigurationType } from "@app/types/assistant/configuration"; import { AgentMessageType, ConversationType, @@ -273,7 +273,7 @@ export type RetrievalParamsEvent = { created: number; configurationId: string; messageId: string; - dataSources: "all" | DataSourceConfiguration[]; + dataSources: AgentDataSourceConfigurationType[]; query: string | null; relativeTimeFrame: TimeFrame | null; topK: number; @@ -387,56 +387,28 @@ export async function* runRetrieval( ); // Handle data sources list and parents/tags filtering. - if (c.dataSources === "all") { - const prodCredentials = await prodAPICredentialsForOwner(owner); - const api = new DustAPI(prodCredentials); + config.DATASOURCE.data_sources = c.dataSources.map((d) => ({ + workspace_id: d.workspaceId, + data_source_id: d.dataSourceId, + })); + + for (const ds of c.dataSources) { + if (ds.filter.tags) { + if (!config.DATASOURCE.filter.tags) { + config.DATASOURCE.filter.tags = { in: [], not: [] }; + } - const dsRes = await api.getDataSources(prodCredentials.workspaceId); - if (dsRes.isErr()) { - return yield { - type: "retrieval_error", - created: Date.now(), - configurationId: configuration.sId, - messageId: agentMessage.sId, - error: { - code: "retrieval_data_sources_error", - message: `Error retrieving workspace data sources: ${dsRes.error.message}`, - }, - }; + config.DATASOURCE.filter.tags.in.push(...ds.filter.tags.in); + config.DATASOURCE.filter.tags.not.push(...ds.filter.tags.not); } - const ds = dsRes.value.filter((d) => d.assistantDefaultSelected); - - config.DATASOURCE.data_sources = ds.map((d) => { - return { - workspace_id: prodCredentials.workspaceId, - data_source_id: d.name, - }; - }); - } else { - config.DATASOURCE.data_sources = c.dataSources.map((d) => ({ - workspace_id: d.workspaceId, - data_source_id: d.dataSourceId, - })); - - for (const ds of c.dataSources) { - if (ds.filter.tags) { - if (!config.DATASOURCE.filter.tags) { - config.DATASOURCE.filter.tags = { in: [], not: [] }; - } - - config.DATASOURCE.filter.tags.in.push(...ds.filter.tags.in); - config.DATASOURCE.filter.tags.not.push(...ds.filter.tags.not); + if (ds.filter.parents) { + if (!config.DATASOURCE.filter.parents) { + config.DATASOURCE.filter.parents = { in: [], not: [] }; } - if (ds.filter.parents) { - if (!config.DATASOURCE.filter.parents) { - config.DATASOURCE.filter.parents = { in: [], not: [] }; - } - - config.DATASOURCE.filter.parents.in.push(...ds.filter.parents.in); - config.DATASOURCE.filter.parents.not.push(...ds.filter.parents.not); - } + config.DATASOURCE.filter.parents.in.push(...ds.filter.parents.in); + config.DATASOURCE.filter.parents.not.push(...ds.filter.parents.not); } } diff --git a/front/lib/api/assistant/agent.ts b/front/lib/api/assistant/agent.ts index 75dce5fe133c..702f0bb1cb61 100644 --- a/front/lib/api/assistant/agent.ts +++ b/front/lib/api/assistant/agent.ts @@ -17,13 +17,8 @@ import { Authenticator } from "@app/lib/auth"; import { Err, Ok, Result } from "@app/lib/result"; import { generateModelSId } from "@app/lib/utils"; import { isRetrievalConfiguration } from "@app/types/assistant/actions/retrieval"; -import { - AgentActionConfigurationType, - AgentActionSpecification, - AgentConfigurationStatus, - AgentConfigurationType, - AgentGenerationConfigurationType, -} from "@app/types/assistant/agent"; +import { AgentActionSpecification } from "@app/types/assistant/agent"; +import { AgentConfigurationType } from "@app/types/assistant/configuration"; import { AgentActionType, AgentMessageType, @@ -31,61 +26,6 @@ import { UserMessageType, } from "@app/types/assistant/conversation"; -/** - * Agent configuration. - */ - -export async function createAgentConfiguration( - auth: Authenticator, - { - name, - pictureUrl, - action, - generation, - }: { - name: string; - pictureUrl?: string; - action?: AgentActionConfigurationType; - generation?: AgentGenerationConfigurationType; - } -): Promise { - return { - sId: generateModelSId(), - name, - pictureUrl: pictureUrl ?? null, - status: "active", - action: action ?? null, - generation: generation ?? null, - }; -} - -export async function updateAgentConfiguration( - auth: Authenticator, - configurationId: string, - { - name, - pictureUrl, - status, - action, - generation, - }: { - name: string; - pictureUrl?: string; - status: AgentConfigurationStatus; - action?: AgentActionConfigurationType; - generation?: AgentGenerationConfigurationType; - } -): Promise { - return { - sId: generateModelSId(), - name, - pictureUrl: pictureUrl ?? null, - status, - action: action ?? null, - generation: generation ?? null, - }; -} - /** * Action Inputs generation. */ diff --git a/front/lib/api/assistant/configuration.ts b/front/lib/api/assistant/configuration.ts new file mode 100644 index 000000000000..f106c5c69698 --- /dev/null +++ b/front/lib/api/assistant/configuration.ts @@ -0,0 +1,684 @@ +import { Op, Transaction } from "sequelize"; + +import { Authenticator } from "@app/lib/auth"; +import { front_sequelize, ModelId } from "@app/lib/databases"; +import { + AgentConfiguration, + AgentDataSourceConfiguration, + AgentGenerationConfiguration, + AgentRetrievalConfiguration, + DataSource, + Workspace, +} from "@app/lib/models"; +import { generateModelSId } from "@app/lib/utils"; +import { + AgentDataSourceConfigurationType, + isTemplatedQuery, + isTimeFrame, + RetrievalQuery, + RetrievalTimeframe, +} from "@app/types/assistant/actions/retrieval"; +import { + AgentActionConfigurationType, + AgentConfigurationStatus, + AgentConfigurationType, + AgentGenerationConfigurationType, +} from "@app/types/assistant/configuration"; + +/** + * Get an agent configuration + */ +export async function getAgentConfiguration( + auth: Authenticator, + agentId: string +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error("Cannot find AgentConfiguration: no workspace."); + } + const agent = await AgentConfiguration.findOne({ + where: { + sId: agentId, + workspaceId: owner.id, + }, + }); + if (!agent) { + throw new Error("Cannot find AgentConfiguration."); + } + + const generationConfig = agent.generationConfigId + ? await AgentGenerationConfiguration.findOne({ + where: { + id: agent.generationConfigId, + }, + }) + : null; + + const actionConfig = agent.retrievalConfigId + ? await AgentRetrievalConfiguration.findOne({ + where: { + id: agent.retrievalConfigId, + }, + }) + : null; + const dataSourcesConfig = actionConfig?.id + ? await AgentDataSourceConfiguration.findAll({ + where: { + retrievalConfigId: actionConfig.id, + }, + }) + : []; + + return { + sId: agent.sId, + name: agent.name, + pictureUrl: agent.pictureUrl, + status: agent.status, + action: actionConfig + ? await renderAgentActionConfigurationType( + actionConfig, + dataSourcesConfig + ) + : null, + generation: generationConfig + ? { + id: generationConfig.id, + prompt: generationConfig.prompt, + model: { + providerId: generationConfig.providerId, + modelId: generationConfig.modelId, + }, + } + : null, + }; +} + +/** + * Create Agent Configuration + */ +export async function createAgentConfiguration( + auth: Authenticator, + { + name, + pictureUrl, + status, + generationConfig, + actionConfig, + }: { + name: string; + pictureUrl: string; + status: AgentConfigurationStatus; + generationConfig: AgentGenerationConfigurationType | null; + actionConfig: AgentActionConfigurationType | null; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error("Cannot create AgentConfiguration: Workspace not found."); + } + + // Create Agent config + const agentConfig = await AgentConfiguration.create({ + sId: generateModelSId(), + status: status, + name: name, + pictureUrl: pictureUrl, + scope: "workspace", + workspaceId: owner.id, + generationConfigId: generationConfig?.id || null, + retrievalConfigId: actionConfig?.id || null, + }); + + return { + sId: agentConfig.sId, + name: agentConfig.name, + pictureUrl: agentConfig.pictureUrl, + status: agentConfig.status, + action: actionConfig, + generation: generationConfig, + }; +} + +/** + * Update Agent Configuration + */ +export async function updateAgentConfiguration( + auth: Authenticator, + agentId: string, + { + name, + pictureUrl, + status, + }: { + name: string; + pictureUrl: string; + status: AgentConfigurationStatus; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error( + "Cannot create AgentGenerationConfiguration: Workspace not found."); + ); + } + const agentConfig = await AgentConfiguration.findOne({ + where: { + sId: agentId, + }, + }); + if (!agentConfig) { + throw new Error( + "Cannot create AgentGenerationConfiguration: Agent not found."); + ); + } + // Updating Agent Config + const updatedAgentConfig = await agentConfig.update({ + name: name, + pictureUrl: pictureUrl, + status: status, + }); + + // Return the config with Generation and Action if any + const existingGeneration = agentConfig.generationConfigId + ? await AgentGenerationConfiguration.findOne({ + where: { + id: agentConfig.generationConfigId, + }, + }) + : null; + + const existingRetrivalConfig = agentConfig.retrievalConfigId + ? await AgentRetrievalConfiguration.findOne({ + where: { + id: agentConfig.retrievalConfigId, + }, + }) + : null; + + const existingDataSourcesConfig = existingRetrivalConfig?.id + ? await AgentDataSourceConfiguration.findAll({ + where: { + retrievalConfigId: existingRetrivalConfig.id, + }, + }) + : []; + + return { + sId: updatedAgentConfig.sId, + name: updatedAgentConfig.name, + pictureUrl: updatedAgentConfig.pictureUrl, + status: updatedAgentConfig.status, + action: existingRetrivalConfig + ? await renderAgentActionConfigurationType( + existingRetrivalConfig, + existingDataSourcesConfig + ) + : null, + generation: existingGeneration + ? { + id: existingGeneration.id, + prompt: existingGeneration.prompt, + model: { + providerId: existingGeneration.providerId, + modelId: existingGeneration.modelId, + }, + } + : null, + }; +} + +/** + * Create Agent Generation Configuration + */ +export async function createAgentGenerationConfiguration( + auth: Authenticator, + { + prompt, + model, + }: { + prompt: string; + model: { + providerId: string; + modelId: string; + }; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error("Cannot create AgentGenerationConfiguration: Workspace not found."); + } + + const genConfig = await AgentGenerationConfiguration.create({ + prompt: prompt, + providerId: model.providerId, + modelId: model.modelId, + }); + + return { + id: genConfig.id, + prompt: genConfig.prompt, + model: { + providerId: genConfig.providerId, + modelId: genConfig.modelId, + }, + }; +} + +/** + * Update Agent Generation Configuration + */ +export async function updateAgentGenerationConfiguration( + auth: Authenticator, + agentId: string, + { + prompt, + model, + }: { + prompt: string; + model: { + providerId: string; + modelId: string; + }; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error( + "Cannot update AgentGenerationConfiguration: Workspace not found."); + ); + } + const agentConfig = await AgentConfiguration.findOne({ + where: { + sId: agentId, + }, + }); + if (!agentConfig) { + throw new Error( + "Cannot update AgentGenerationConfiguration: Agent not found."); + ); + } + + if (!agentConfig.generationConfigId) { + throw new Error( + "Cannot update AgentGenerationConfiguration: Agent has no config."); + ); + } + const existingGenConfig = await AgentGenerationConfiguration.findOne({ + where: { + id: agentConfig.generationConfigId, + }, + }); + if (!existingGenConfig) { + throw new Error( + "Cannot update AgentGenerationConfiguration: config not found."); + ); + } + + const updatedGenConfig = await existingGenConfig.update({ + prompt: prompt, + providerId: model.providerId, + modelId: model.modelId, + }); + + return { + id: updatedGenConfig.id, + prompt: updatedGenConfig.prompt, + model: { + providerId: updatedGenConfig.providerId, + modelId: updatedGenConfig.modelId, + }, + }; +} + +/** + * Create Agent RetrievalConfiguration + */ +export async function createAgentActionConfiguration( + auth: Authenticator, + { + type, + query, + timeframe, + topK, + dataSources, + }: { + type: string; + query: RetrievalQuery; + timeframe: RetrievalTimeframe; + topK: number; + dataSources: AgentDataSourceConfigurationType[]; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error("Cannot create AgentActionConfiguration: no workspace"); + } + + return await front_sequelize.transaction(async (t) => { + let retrievalConfig: AgentRetrievalConfiguration | null = null; + let dataSourcesConfig: AgentDataSourceConfiguration[] = []; + + if (type !== "retrieval_configuration") { + throw new Error("Cannot create AgentActionConfiguration: unknow type"); + } + + // Create Retrieval & Datasources configs + retrievalConfig = await AgentRetrievalConfiguration.create( + { + query: isTemplatedQuery(query) ? "templated" : query, + queryTemplate: isTemplatedQuery(query) ? query.template : null, + relativeTimeFrame: isTimeFrame(timeframe) ? "custom" : timeframe, + relativeTimeFrameDuration: isTimeFrame(timeframe) + ? timeframe.duration + : null, + relativeTimeFrameUnit: isTimeFrame(timeframe) ? timeframe.unit : null, + topK: topK, + }, + { transaction: t } + ); + dataSourcesConfig = await _createAgentDataSourcesConfigData( + t, + dataSources, + retrievalConfig.id + ); + + return await renderAgentActionConfigurationType( + retrievalConfig, + dataSourcesConfig + ); + }); +} + +/** + * Update Agent Retrieval Configuration + * This will destroy and recreate the retrieval config + */ +export async function updateAgentActionConfiguration( + auth: Authenticator, + agentId: string, + { + type, + query, + timeframe, + topK, + dataSources, + }: { + type: string; + query: RetrievalQuery; + timeframe: RetrievalTimeframe; + topK: number; + dataSources: AgentDataSourceConfigurationType[]; + } +): Promise { + const owner = auth.workspace(); + if (!owner) { + throw new Error( + "Cannot update AgentActionConfiguration: Workspace not found."); + ); + } + if (type !== "retrieval_configuration") { + throw new Error("Unkown Agent action type"); + } + + const agentConfig = await AgentConfiguration.findOne({ + where: { + sId: agentId, + }, + }); + if (!agentConfig) { + throw new Error( + "Cannot update AgentActionConfiguration: Agent not found."); + ); + } + + if (!agentConfig.retrievalConfigId) { + throw new Error( + "Cannot update AgentActionConfiguration: Agent has no retrieval config."); + ); + } + const existingRetrievalConfig = await AgentRetrievalConfiguration.findOne({ + where: { + id: agentConfig.retrievalConfigId, + }, + }); + if (!existingRetrievalConfig) { + throw new Error( + "Cannot update AgentActionConfiguration: config not found."); + ); + } + + return await front_sequelize.transaction(async (t) => { + const updatedRetrievalConfig = await existingRetrievalConfig.update( + { + query: isTemplatedQuery(query) ? "templated" : query, + queryTemplate: isTemplatedQuery(query) ? query.template : null, + relativeTimeFrame: isTimeFrame(timeframe) ? "custom" : timeframe, + relativeTimeFrameDuration: isTimeFrame(timeframe) + ? timeframe.duration + : null, + relativeTimeFrameUnit: isTimeFrame(timeframe) ? timeframe.unit : null, + topK: topK, + }, + { transaction: t } + ); + + // Destroy existing dataSources config + await AgentDataSourceConfiguration.destroy({ + where: { + retrievalConfigId: existingRetrievalConfig.id, + }, + }); + + // Create new dataSources config + const dataSourcesConfig = await _createAgentDataSourcesConfigData( + t, + dataSources, + updatedRetrievalConfig.id + ); + + return await renderAgentActionConfigurationType( + updatedRetrievalConfig, + dataSourcesConfig + ); + }); +} + +/** + * Builds the agent action configuration type from the model + */ +async function renderAgentActionConfigurationType( + action: AgentRetrievalConfiguration, + dataSourcesConfig: AgentDataSourceConfiguration[] +): Promise { + // Build Retrieval Timeframe + let timeframe: RetrievalTimeframe = "auto"; + if ( + action.relativeTimeFrame === "custom" && + action.relativeTimeFrameDuration && + action.relativeTimeFrameUnit + ) { + timeframe = { + duration: action.relativeTimeFrameDuration, + unit: action.relativeTimeFrameUnit, + }; + } else if (action.relativeTimeFrame === "none") { + timeframe = "none"; + } + + // Build Retrieval Query + let query: RetrievalQuery = "auto"; + if (action.query === "templated" && action.queryTemplate) { + query = { + template: action.queryTemplate, + }; + } else if (action.query === "none") { + query = "none"; + } + + // Build Retrieval DataSources + const dataSourcesIds = dataSourcesConfig?.map((ds) => ds.dataSourceId); + const dataSources = await DataSource.findAll({ + where: { + id: { [Op.in]: dataSourcesIds }, + }, + attributes: ["id", "name", "workspaceId"], + }); + const workspaceIds = dataSources.map((ds) => ds.workspaceId); + const workspaces = await Workspace.findAll({ + where: { + id: { [Op.in]: workspaceIds }, + }, + attributes: ["id", "sId"], + }); + + let dataSource: DataSource | undefined; + let workspace: Workspace | undefined; + const dataSourcesConfigType: AgentDataSourceConfigurationType[] = []; + + dataSourcesConfig.forEach(async (dsConfig) => { + dataSource = dataSources.find((ds) => ds.id === dsConfig.dataSourceId); + workspace = workspaces.find((w) => w.id === dataSource?.workspaceId); + + if (!dataSource || !workspace) { + throw new Error("Can't render Agent Retrieval dataSources: not found."); + } + + dataSourcesConfigType.push({ + dataSourceId: dataSource.name, + workspaceId: workspace.sId, + filter: { + tags: + dsConfig.tagsIn && dsConfig.tagsNotIn + ? { in: dsConfig.tagsIn, not: dsConfig.tagsNotIn } + : null, + parents: + dsConfig.parentsIn && dsConfig.parentsNotIn + ? { in: dsConfig.parentsIn, not: dsConfig.parentsNotIn } + : null, + }, + }); + }); + + return { + id: action.id, + type: "retrieval_configuration", + query: query, + relativeTimeFrame: timeframe, + topK: action.topK, + dataSources: dataSourcesConfigType, + }; +} + +/** + * Create the AgentDataSourceConfiguration rows in database. + * + * Knowing that a datasource is uniquely identified by its name and its workspaceId + * We need to fetch the dataSources from the database from that. + * We obvisously need to do as few queries as possible. + */ +async function _createAgentDataSourcesConfigData( + t: Transaction, + dataSourcesConfig: AgentDataSourceConfigurationType[], + retrievalConfigId: number +): Promise { + // dsConfig contains this format: + // [ + // { workspaceSId: s1o1u1p, dataSourceName: "managed-notion", filter: { tags: null, parents: null } }, + // { workspaceSId: s1o1u1p, dataSourceName: "managed-slack", filter: { tags: null, parents: null } }, + // { workspaceSId: i2n2o2u, dataSourceName: "managed-notion", filter: { tags: null, parents: null } }, + // ] + + // First we get the list of workspaces because we need the mapping between workspaceSId and workspaceId + const workspaces = await Workspace.findAll({ + where: { + sId: dataSourcesConfig.map((dsConfig) => dsConfig.workspaceId), + }, + attributes: ["id", "sId"], + }); + + // Now will want to group the datasource names by workspaceId to do only one query per workspace. + // We want this: + // [ + // { workspaceId: 1, dataSourceNames: [""managed-notion", "managed-slack"] }, + // { workspaceId: 2, dataSourceNames: ["managed-notion"] } + // ] + type _DsNamesPerWorkspaceIdType = { + workspaceId: number; + dataSourceNames: string[]; + }; + const dsNamesPerWorkspaceId = dataSourcesConfig.reduce( + ( + acc: _DsNamesPerWorkspaceIdType[], + curr: AgentDataSourceConfigurationType + ) => { + // First we need to get the workspaceId from the workspaceSId + const workspace = workspaces.find((w) => w.sId === curr.workspaceId); + if (!workspace) { + throw new Error( + "Can't create Datasources config for retrieval: Workspace not found" + ); + } + + // Find an existing entry for this workspaceId + const existingEntry: _DsNamesPerWorkspaceIdType | undefined = acc.find( + (entry: _DsNamesPerWorkspaceIdType) => + entry.workspaceId === workspace.id + ); + if (existingEntry) { + // Append dataSourceName to existing entry + existingEntry.dataSourceNames.push(curr.dataSourceId); + } else { + // Add a new entry for this workspaceId + acc.push({ + workspaceId: workspace.id, + dataSourceNames: [curr.dataSourceId], + }); + } + return acc; + }, + [] + ); + + // Then we get do one findAllQuery per workspaceId, in a Promise.all + const getDataSourcesQueries = dsNamesPerWorkspaceId.map( + ({ workspaceId, dataSourceNames }) => { + return DataSource.findAll({ + where: { + workspaceId, + name: { + [Op.in]: dataSourceNames, + }, + }, + }); + } + ); + const results = await Promise.all(getDataSourcesQueries); + const dataSources = results.flat(); + + const agentDataSourcesConfigRows: AgentDataSourceConfiguration[] = + await Promise.all( + dataSourcesConfig.map(async (dsConfig) => { + const dataSource = dataSources.find( + (ds) => + ds.name === dsConfig.dataSourceId && + ds.workspaceId === + workspaces.find((w) => w.sId === dsConfig.workspaceId)?.id + ); + if (!dataSource) { + throw new Error("Can't create AgentDataSourcesConfig: datasource not found."); + } + return AgentDataSourceConfiguration.create( + { + dataSourceId: dataSource.id, + tagsIn: dsConfig.filter.tags?.in, + tagsNotIn: dsConfig.filter.tags?.not, + parentsIn: dsConfig.filter.parents?.in, + parentsNotIn: dsConfig.filter.parents?.not, + retrievalConfigId: retrievalConfigId, + }, + { transaction: t } + ); + }) + ); + return agentDataSourcesConfigRows; +} diff --git a/front/lib/api/assistant/generation.ts b/front/lib/api/assistant/generation.ts index 70bdb71160ee..c4a3c6c3a2da 100644 --- a/front/lib/api/assistant/generation.ts +++ b/front/lib/api/assistant/generation.ts @@ -9,7 +9,7 @@ import { CoreAPI } from "@app/lib/core_api"; import { Err, Ok, Result } from "@app/lib/result"; import logger from "@app/logger/logger"; import { isRetrievalActionType } from "@app/types/assistant/actions/retrieval"; -import { AgentConfigurationType } from "@app/types/assistant/agent"; +import { AgentConfigurationType } from "@app/types/assistant/configuration"; import { AgentMessageType, ConversationType, diff --git a/front/lib/models/assistant/actions/retrieval.ts b/front/lib/models/assistant/actions/retrieval.ts index f12372810306..6394c461465c 100644 --- a/front/lib/models/assistant/actions/retrieval.ts +++ b/front/lib/models/assistant/actions/retrieval.ts @@ -8,200 +8,9 @@ import { } from "sequelize"; import { front_sequelize } from "@app/lib/databases"; -import { AgentConfiguration } from "@app/lib/models/assistant/agent"; -import { DataSource } from "@app/lib/models/data_source"; +import { AgentRetrievalConfiguration } from "@app/lib/models/assistant/configuration"; import { TimeframeUnit } from "@app/types/assistant/actions/retrieval"; -/** - * Action Retrieval configuration - */ -export class AgentRetrievalConfiguration extends Model< - InferAttributes, - InferCreationAttributes -> { - declare id: CreationOptional; - declare createdAt: CreationOptional; - declare updatedAt: CreationOptional; - - declare query: "auto" | "none" | "templated"; - declare queryTemplate: string | null; - declare relativeTimeFrame: "auto" | "none" | "custom"; - declare relativeTimeFrameDuration: number | null; - declare relativeTimeFrameUnit: TimeframeUnit | null; - declare topK: number; - - declare agentId: ForeignKey; -} -AgentRetrievalConfiguration.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - query: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: "auto", - }, - queryTemplate: { - type: DataTypes.TEXT, - allowNull: true, - }, - relativeTimeFrame: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: "auto", - }, - relativeTimeFrameDuration: { - type: DataTypes.INTEGER, - allowNull: true, - }, - relativeTimeFrameUnit: { - type: DataTypes.STRING, - allowNull: true, - }, - topK: { - type: DataTypes.INTEGER, - allowNull: false, - }, - }, - { - modelName: "agent_retrieval_configuration", - sequelize: front_sequelize, - hooks: { - beforeValidate: (retrieval: AgentRetrievalConfiguration) => { - // Validation for templated Query - if (retrieval.query == "templated") { - if (retrieval.queryTemplate === null) { - throw new Error("Must set a template for templated query"); - } - } else if (retrieval.queryTemplate !== null) { - throw new Error("Can't set a template without templated query"); - } - - // Validation for Timeframe - if (retrieval.relativeTimeFrame == "custom") { - if ( - retrieval.relativeTimeFrameDuration === null || - retrieval.relativeTimeFrameUnit === null - ) { - throw new Error( - "Custom relative time frame must have a duration and unit set" - ); - } - } - }, - }, - } -); - -/** - * Configuration of Datasources used for Retrieval Action. - */ -export class AgentDataSourceConfiguration extends Model< - InferAttributes, - InferCreationAttributes -> { - declare id: CreationOptional; - declare createdAt: CreationOptional; - declare updatedAt: CreationOptional; - - declare timeframeDuration: number | null; - declare timeframeUnit: TimeframeUnit | null; - - declare tagsIn: string[] | null; - declare tagsNotIn: string[] | null; - declare parentsIn: string[] | null; - declare parentsNotIn: string[] | null; - - declare dataSourceId: ForeignKey; - declare retrievalConfigurationId: ForeignKey< - AgentRetrievalConfiguration["id"] - >; -} -AgentDataSourceConfiguration.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - timeframeDuration: { - type: DataTypes.INTEGER, - allowNull: true, - }, - timeframeUnit: { - type: DataTypes.STRING, - allowNull: true, - }, - tagsIn: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - }, - tagsNotIn: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - }, - parentsIn: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - }, - parentsNotIn: { - type: DataTypes.ARRAY(DataTypes.STRING), - allowNull: true, - }, - }, - { - modelName: "agent_data_source_configuration", - sequelize: front_sequelize, - hooks: { - beforeValidate: (dataSourceConfig: AgentDataSourceConfiguration) => { - if ( - (dataSourceConfig.timeframeDuration === null) !== - (dataSourceConfig.timeframeUnit === null) - ) { - throw new Error( - "Timeframe duration/unit must be both set or both null" - ); - } - }, - }, - } -); - -// Retrieval config <> data source config -AgentRetrievalConfiguration.hasMany(AgentDataSourceConfiguration, { - foreignKey: { name: "retrievalId", allowNull: false }, - onDelete: "CASCADE", -}); - -// Agent config <> Retrieval config -AgentConfiguration.hasOne(AgentRetrievalConfiguration, { - foreignKey: { name: "agentId", allowNull: true }, // null = no generation set for this Agent - onDelete: "CASCADE", -}); - /** * Retrieval Action */ diff --git a/front/lib/models/assistant/agent.ts b/front/lib/models/assistant/agent.ts deleted file mode 100644 index c10393cceac4..000000000000 --- a/front/lib/models/assistant/agent.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - CreationOptional, - DataTypes, - ForeignKey, - InferAttributes, - InferCreationAttributes, - Model, -} from "sequelize"; - -import { front_sequelize } from "@app/lib/databases"; -import { AgentRetrievalConfiguration } from "@app/lib/models/assistant/actions/retrieval"; -import { Workspace } from "@app/lib/models/workspace"; -import { - AgentConfigurationScope, - AgentConfigurationStatus, -} from "@app/types/assistant/agent"; - -/** - * Agent configuration - */ -export class AgentConfiguration extends Model< - InferAttributes, - InferCreationAttributes -> { - declare id: CreationOptional; - declare createdAt: CreationOptional; - declare updatedAt: CreationOptional; - - declare sId: string; - declare status: AgentConfigurationStatus; - declare name: string; - declare pictureUrl: string | null; - - declare scope: AgentConfigurationScope; - declare workspaceId: ForeignKey | null; // null = it's a global agent - - declare model: ForeignKey | null; -} -AgentConfiguration.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - sId: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - status: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: "active", - }, - name: { - type: DataTypes.TEXT, - allowNull: false, - }, - pictureUrl: { - type: DataTypes.TEXT, - allowNull: true, - }, - scope: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: "workspace", - }, - }, - { - modelName: "agent_configuration", - sequelize: front_sequelize, - indexes: [ - { fields: ["workspaceId"] }, - // Unique name per workspace. - // Note that on PostgreSQL a unique constraint on multiple columns will treat NULL - // as distinct from any other value, so we can create twice the same name if at least - // one of the workspaceId is null. We're okay with it. - { fields: ["workspaceId", "name", "scope"], unique: true }, - { fields: ["sId"], unique: true }, - ], - hooks: { - beforeValidate: (agent: AgentConfiguration) => { - if (agent.scope !== "workspace" && agent.workspaceId) { - throw new Error("Workspace id must be null for global agent"); - } else if (agent.scope === "workspace" && !agent.workspaceId) { - throw new Error("Workspace id must be set for non-global agent"); - } - }, - }, - } -); - -/** - * Configuration of Agent generation. - */ -export class AgentGenerationConfiguration extends Model< - InferAttributes, - InferCreationAttributes -> { - declare id: CreationOptional; - declare createdAt: CreationOptional; - declare updatedAt: CreationOptional; - - declare prompt: string; - declare modelProvider: string; - declare modelId: string; - - declare agentId: ForeignKey; -} -AgentGenerationConfiguration.init( - { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - prompt: { - type: DataTypes.TEXT, - allowNull: false, - }, - modelProvider: { - type: DataTypes.STRING, - allowNull: false, - }, - modelId: { - type: DataTypes.STRING, - allowNull: false, - }, - }, - { - modelName: "agent_generation_configuration", - sequelize: front_sequelize, - } -); - -// Workspace <> Agent config -Workspace.hasMany(AgentConfiguration, { - foreignKey: { name: "workspaceId", allowNull: true }, // null = global Agent - onDelete: "CASCADE", -}); - -// Agent config <> Generation config -AgentConfiguration.hasOne(AgentGenerationConfiguration, { - foreignKey: { name: "agentId", allowNull: false }, // null = no retrieval action set for this Agent - onDelete: "CASCADE", -}); diff --git a/front/lib/models/assistant/configuration.ts b/front/lib/models/assistant/configuration.ts new file mode 100644 index 000000000000..0cf66ac3de67 --- /dev/null +++ b/front/lib/models/assistant/configuration.ts @@ -0,0 +1,352 @@ +import { + CreationOptional, + DataTypes, + ForeignKey, + InferAttributes, + InferCreationAttributes, + Model, +} from "sequelize"; + +import { front_sequelize } from "@app/lib/databases"; +import { DataSource } from "@app/lib/models/data_source"; +import { Workspace } from "@app/lib/models/workspace"; +import { TimeframeUnit } from "@app/types/assistant/actions/retrieval"; +import { + AgentConfigurationScope, + AgentConfigurationStatus, +} from "@app/types/assistant/configuration"; + +/** + * Agent configuration + */ +export class AgentConfiguration extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare createdAt: CreationOptional; + declare updatedAt: CreationOptional; + + declare sId: string; + declare status: AgentConfigurationStatus; + declare name: string; + declare pictureUrl: string | null; + declare scope: AgentConfigurationScope; + + declare workspaceId: ForeignKey | null; // null = it's a global agent + declare generationConfigId: ForeignKey< + AgentGenerationConfiguration["id"] + > | null; + declare retrievalConfigId: ForeignKey< + AgentRetrievalConfiguration["id"] + > | null; +} +AgentConfiguration.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + sId: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + status: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "active", + }, + name: { + type: DataTypes.TEXT, + allowNull: false, + }, + pictureUrl: { + type: DataTypes.TEXT, + allowNull: true, + }, + scope: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "workspace", + }, + }, + { + modelName: "agent_configuration", + sequelize: front_sequelize, + indexes: [ + { fields: ["workspaceId"] }, + // Unique name per workspace. + // Note that on PostgreSQL a unique constraint on multiple columns will treat NULL + // as distinct from any other value, so we can create twice the same name if at least + // one of the workspaceId is null. We're okay with it. + { fields: ["workspaceId", "name", "scope"], unique: true }, + { fields: ["sId"], unique: true }, + ], + hooks: { + beforeValidate: (agent: AgentConfiguration) => { + if (agent.scope !== "workspace" && agent.workspaceId) { + throw new Error("Workspace id must be null for global agent"); + } else if (agent.scope === "workspace" && !agent.workspaceId) { + throw new Error("Workspace id must be set for non-global agent"); + } + }, + }, + } +); + +/** + * Configuration of Agent generation. + */ +export class AgentGenerationConfiguration extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare createdAt: CreationOptional; + declare updatedAt: CreationOptional; + + declare prompt: string; + declare providerId: string; + declare modelId: string; +} +AgentGenerationConfiguration.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + prompt: { + type: DataTypes.TEXT, + allowNull: false, + }, + providerId: { + type: DataTypes.STRING, + allowNull: false, + }, + modelId: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + modelName: "agent_generation_configuration", + sequelize: front_sequelize, + } +); + +/** + * Action Retrieval configuration + */ +export class AgentRetrievalConfiguration extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare createdAt: CreationOptional; + declare updatedAt: CreationOptional; + + declare query: "auto" | "none" | "templated"; + declare queryTemplate: string | null; + declare relativeTimeFrame: "auto" | "none" | "custom"; + declare relativeTimeFrameDuration: number | null; + declare relativeTimeFrameUnit: TimeframeUnit | null; + declare topK: number; +} +AgentRetrievalConfiguration.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + query: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "auto", + }, + queryTemplate: { + type: DataTypes.TEXT, + allowNull: true, + }, + relativeTimeFrame: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "auto", + }, + relativeTimeFrameDuration: { + type: DataTypes.INTEGER, + allowNull: true, + }, + relativeTimeFrameUnit: { + type: DataTypes.STRING, + allowNull: true, + }, + topK: { + type: DataTypes.INTEGER, + allowNull: false, + }, + }, + { + modelName: "agent_retrieval_configuration", + sequelize: front_sequelize, + hooks: { + beforeValidate: (retrieval: AgentRetrievalConfiguration) => { + // Validation for templated Query + if (retrieval.query == "templated") { + if (retrieval.queryTemplate === null) { + throw new Error("Must set a template for templated query"); + } + } else if (retrieval.queryTemplate !== null) { + throw new Error("Can't set a template without templated query"); + } + + // Validation for Timeframe + if (retrieval.relativeTimeFrame == "custom") { + if ( + retrieval.relativeTimeFrameDuration === null || + retrieval.relativeTimeFrameUnit === null + ) { + throw new Error( + "Custom relative time frame must have a duration and unit set" + ); + } + } + }, + }, + } +); + +/** + * Configuration of Datasources used for Retrieval Action. + */ +export class AgentDataSourceConfiguration extends Model< + InferAttributes, + InferCreationAttributes +> { + declare id: CreationOptional; + declare createdAt: CreationOptional; + declare updatedAt: CreationOptional; + + declare tagsIn: string[] | null; + declare tagsNotIn: string[] | null; + declare parentsIn: string[] | null; + declare parentsNotIn: string[] | null; + + declare dataSourceId: ForeignKey; + declare retrievalConfigId: ForeignKey; +} +AgentDataSourceConfiguration.init( + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + tagsIn: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + }, + tagsNotIn: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + }, + parentsIn: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + }, + parentsNotIn: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + }, + }, + { + modelName: "agent_data_source_configuration", + sequelize: front_sequelize, + hooks: { + beforeValidate: (dataSourceConfig: AgentDataSourceConfiguration) => { + if ( + (dataSourceConfig.tagsIn === null) !== + (dataSourceConfig.tagsNotIn === null) + ) { + throw new Error("Tags must be both set or both null"); + } + if ( + (dataSourceConfig.parentsIn === null) !== + (dataSourceConfig.parentsNotIn === null) + ) { + throw new Error("Parents must be both set or both null"); + } + }, + }, + } +); + +// Agent config <> Workspace +Workspace.hasMany(AgentConfiguration, { + foreignKey: { name: "workspaceId", allowNull: true }, // null = global Agent + onDelete: "CASCADE", +}); + +// Agent config <> Generation config +AgentGenerationConfiguration.hasOne(AgentConfiguration, { + foreignKey: { name: "generationConfigId", allowNull: true }, // null = no generation set for this Agent +}); +// Agent config <> Retrieval config +AgentRetrievalConfiguration.hasOne(AgentConfiguration, { + foreignKey: { name: "retrievalConfigId", allowNull: true }, // null = no retrieval action set for this Agent +}); + +// Retrieval config <> Data source config +AgentRetrievalConfiguration.hasOne(AgentDataSourceConfiguration, { + foreignKey: { name: "retrievalConfigId", allowNull: false }, + onDelete: "CASCADE", +}); + +// Data source config <> Data source +DataSource.hasMany(AgentDataSourceConfiguration, { + foreignKey: { name: "dataSourceId", allowNull: false }, + onDelete: "CASCADE", +}); diff --git a/front/lib/models/index.ts b/front/lib/models/index.ts index 0bc9edbbbdc0..d15b932065a2 100644 --- a/front/lib/models/index.ts +++ b/front/lib/models/index.ts @@ -1,15 +1,15 @@ import { App, Clone, Dataset, Provider, Run } from "@app/lib/models/apps"; import { - AgentDataSourceConfiguration, AgentRetrievalAction, - AgentRetrievalConfiguration, RetrievalDocument, RetrievalDocumentChunk, } from "@app/lib/models/assistant/actions/retrieval"; import { AgentConfiguration, + AgentDataSourceConfiguration, AgentGenerationConfiguration, -} from "@app/lib/models/assistant/agent"; + AgentRetrievalConfiguration, +} from "@app/lib/models/assistant/configuration"; import { AgentMessage, Conversation, diff --git a/front/types/assistant/actions/retrieval.ts b/front/types/assistant/actions/retrieval.ts index 2544edeb7ee7..42d0935d920d 100644 --- a/front/types/assistant/actions/retrieval.ts +++ b/front/types/assistant/actions/retrieval.ts @@ -1,9 +1,5 @@ -/** - * Data Source configuration - */ - import { ModelId } from "@app/lib/databases"; -import { AgentActionConfigurationType } from "@app/types/assistant/agent"; +import { AgentActionConfigurationType } from "@app/types/assistant/configuration"; import { AgentActionType } from "@app/types/assistant/conversation"; export type TimeframeUnit = "hour" | "day" | "week" | "month" | "year"; @@ -12,23 +8,47 @@ export type TimeFrame = { unit: TimeframeUnit; }; -export type DataSourceFilter = { - tags: { in: string[]; not: string[] } | null; - parents: { in: string[]; not: string[] } | null; +export type TemplatedQuery = { + template: string; }; +export function isTemplatedQuery(arg: RetrievalQuery): arg is TemplatedQuery { + return (arg as TemplatedQuery).template !== undefined; +} +export function isTimeFrame(arg: RetrievalTimeframe): arg is TimeFrame { + return ( + (arg as TimeFrame).duration !== undefined && + (arg as TimeFrame).unit !== undefined + ); +} -export type DataSourceConfiguration = { - workspaceId: string; - dataSourceId: string; - filter: DataSourceFilter; +/** + * Retrieval Action config + */ +export type RetrievalConfigurationType = { + id: ModelId; + + type: "retrieval_configuration"; + dataSources: AgentDataSourceConfigurationType[]; + query: RetrievalQuery; + relativeTimeFrame: RetrievalTimeframe; + topK: number; }; +export function isRetrievalConfiguration( + arg: AgentActionConfigurationType | null +): arg is RetrievalConfigurationType { + return arg !== null && arg.type && arg.type === "retrieval_configuration"; +} /** - * Retrieval configuration + * Datasources config for Retrieval Action */ - -export type TemplatedQuery = { - template: string; +export type AgentDataSourceConfigurationType = { + workspaceId: string; // need sId to talk with Core (external id) + dataSourceId: string; // need Datasource.name to talk with Core (external id) + filter: { + tags: { in: string[]; not: string[] } | null; + parents: { in: string[]; not: string[] } | null; + }; }; // Retrieval specifies a list of data sources (with possible parent / tags filtering, possible "all" @@ -39,24 +59,8 @@ export type TemplatedQuery = { // `query` and `relativeTimeFrame` will be used to generate the inputs specification for the model // in charge of generating the action inputs. The results will be used along with `topK` and // `dataSources` to query the data. -export type RetrievalConfigurationType = { - id: ModelId; - - type: "retrieval_configuration"; - dataSources: "all" | DataSourceConfiguration[]; - query: "auto" | "none" | TemplatedQuery; - relativeTimeFrame: "auto" | "none" | TimeFrame; - topK: number; - - // Dynamically decide to skip, if needed in the future - // autoSkip: boolean; -}; - -export function isRetrievalConfiguration( - arg: AgentActionConfigurationType | null -): arg is RetrievalConfigurationType { - return arg !== null && arg.type && arg.type === "retrieval_configuration"; -} +export type RetrievalTimeframe = "auto" | "none" | TimeFrame; +export type RetrievalQuery = "auto" | "none" | TemplatedQuery; /** * Retrieval action @@ -88,7 +92,7 @@ export type RetrievalActionType = { id: ModelId; // AgentRetrieval. type: "retrieval_action"; params: { - dataSources: "all" | DataSourceConfiguration[]; + dataSources: AgentDataSourceConfigurationType[]; relativeTimeFrame: TimeFrame | null; query: string | null; topK: number; diff --git a/front/types/assistant/agent.ts b/front/types/assistant/agent.ts index 457320076e1a..0ea0e0096821 100644 --- a/front/types/assistant/agent.ts +++ b/front/types/assistant/agent.ts @@ -1,34 +1,7 @@ -import { RetrievalConfigurationType } from "@app/types/assistant/actions/retrieval"; - /** - * Agent Action configuration + * Agent Action */ -// New AgentActionConfigurationType checklist: -// - Add the type to the union type below -// - Add model rendering support in `renderConversationForModel` -export type AgentActionConfigurationType = RetrievalConfigurationType; - -// Each AgentActionConfigurationType is capable of generating this type at runtime to specify which -// inputs should be generated by the model. As an example, to run the retrieval action for which the -// `relativeTimeFrame` has been specified in the configuration but for which the `query` is "auto", -// it would generate: -// -// ``` -// { inputs: [{ name: "query", description: "...", type: "string" }] -// ``` -// -// The params generator model for this action would be tasked to generate that query. If the -// retrieval configuration sets `relativeTimeFrame` to "auto" as well we would get: -// -// ``` -// { -// inputs: [ -// { name: "query", description: "...", type: "string" }, -// { name: "relativeTimeFrame", description: "...", type: "string" }, -// ] -// } -// ``` export type AgentActionSpecification = { name: string; description: string; @@ -38,37 +11,3 @@ export type AgentActionSpecification = { type: "string" | "number" | "boolean"; }[]; }; - -/** - * Agent Message configuration - */ - -export type AgentGenerationConfigurationType = { - prompt: string; - model: { - providerId: string; - modelId: string; - }; -}; - -/** - * Agent configuration - */ - -export type AgentConfigurationStatus = "active" | "archived"; -export type AgentConfigurationScope = "global" | "workspace"; - -export type AgentConfigurationType = { - sId: string; - status: AgentConfigurationStatus; - - name: string; - pictureUrl: string | null; - - // If undefined, no action performed, otherwise the action is - // performed (potentially NoOp eg autoSkip above). - action: AgentActionConfigurationType | null; - - // If undefined, no text generation. - generation: AgentGenerationConfigurationType | null; -}; diff --git a/front/types/assistant/configuration.ts b/front/types/assistant/configuration.ts new file mode 100644 index 000000000000..002bc69371cd --- /dev/null +++ b/front/types/assistant/configuration.ts @@ -0,0 +1,57 @@ +import { ModelId } from "@app/lib/databases"; +import { + RetrievalConfigurationType, + RetrievalQuery, + RetrievalTimeframe, +} from "@app/types/assistant/actions/retrieval"; + +/** + * Agent config + */ +export type AgentConfigurationStatus = "active" | "archived"; +export type AgentConfigurationScope = "global" | "workspace"; +export type AgentConfigurationType = { + sId: string; + status: AgentConfigurationStatus; + name: string; + pictureUrl: string | null; + action: AgentActionConfigurationType | null; // If undefined, no action performed + generation: AgentGenerationConfigurationType | null; // If undefined, no text generation. +}; + +/** + * Generation config + */ +export type AgentGenerationConfigurationType = { + id: ModelId; + prompt: string; + model: { + providerId: string; + modelId: string; + }; +}; + +/** + * Action > Retrieval + */ +// Each AgentActionConfigurationType is capable of generating this type at runtime to specify which +// inputs should be generated by the model. As an example, to run the retrieval action for which the +// `relativeTimeFrame` has been specified in the configuration but for which the `query` is "auto", +// it would generate: +// +// ``` +// { inputs: [{ name: "query", description: "...", type: "string" }] +// ``` +// +// The params generator model for this action would be tasked to generate that query. If the +// retrieval configuration sets `relativeTimeFrame` to "auto" as well we would get: +// +// ``` +// { +// inputs: [ +// { name: "query", description: "...", type: "string" }, +// { name: "relativeTimeFrame", description: "...", type: "string" }, +// ] +// } +// ``` +export type AgentActionConfigurationType = RetrievalConfigurationType; diff --git a/front/types/assistant/conversation.ts b/front/types/assistant/conversation.ts index 14ff04663172..342d1f8a246f 100644 --- a/front/types/assistant/conversation.ts +++ b/front/types/assistant/conversation.ts @@ -1,8 +1,8 @@ import { ModelId } from "@app/lib/databases"; +import { AgentConfigurationType } from "@app/types/assistant/configuration"; import { UserType } from "@app/types/user"; import { RetrievalActionType } from "./actions/retrieval"; -import { AgentConfigurationType } from "./agent"; /** * Mentions