mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
🪨 feat: AWS Bedrock support (#3935)
* feat: Add BedrockIcon component to SVG library * feat: EModelEndpoint.bedrock * feat: first pass, bedrock chat. note: AgentClient is returning `agents` as conversation.endpoint * fix: declare endpoint in initialization step * chore: Update @librechat/agents dependency to version 1.4.5 * feat: backend content aggregation for agents/bedrock * feat: abort agent requests * feat: AWS Bedrock icons * WIP: agent provider schema parsing * chore: Update EditIcon props type * refactor(useGenerationsByLatest): make agents and bedrock editable * refactor: non-assistant message content, parts * fix: Bedrock response `sender` * fix: use endpointOption.model_parameters not endpointOption.modelOptions * fix: types for step handler * refactor: Update Agents.ToolCallDelta type * refactor: Remove unnecessary assignment of parentMessageId in AskController * refactor: remove unnecessary assignment of parentMessageId (agent request handler) * fix(bedrock/agents): message regeneration * refactor: dynamic form elements using react-hook-form Controllers * fix: agent icons/labels for messages * fix: agent actions * fix: use of new dynamic tags causing application crash * refactor: dynamic settings touch-ups * refactor: update Slider component to allow custom track class name * refactor: update DynamicSlider component styles * refactor: use Constants value for GLOBAL_PROJECT_NAME (enum) * feat: agent share global methods/controllers * fix: agents query * fix: `getResponseModel` * fix: share prompt a11y issue * refactor: update SharePrompt dialog theme styles * refactor: explicit typing for SharePrompt * feat: add agent roles/permissions * chore: update @librechat/agents dependency to version 1.4.7 for tool_call_ids edge case * fix(Anthropic): messages.X.content.Y.tool_use.input: Input should be a valid dictionary * fix: handle text parts with tool_call_ids and empty text * fix: role initialization * refactor: don't make instructions required * refactor: improve typing of Text part * fix: setShowStopButton for agents route * chore: remove params for now * fix: add streamBuffer and streamRate to help prevent 'Overloaded' errors from Anthropic API * refactor: remove console.log statement in ContentRender component * chore: typing, rename Context to Delete Button * chore(DeleteButton): logging * refactor(Action): make accessible * style(Action): improve a11y again * refactor: remove use/mention of mongoose sessions * feat: first pass, sharing agents * feat: visual indicator for global agent, remove author when serving to non-author * wip: params * chore: fix typing issues * fix(schemas): typing * refactor: improve accessibility of ListCard component and fix console React warning * wip: reset templates for non-legacy new convos * Revert "wip: params" This reverts commitf8067e91d4. * Revert "refactor: dynamic form elements using react-hook-form Controllers" This reverts commit2150c4815d. * fix(Parameters): types and parameter effect update to only update local state to parameters * refactor: optimize useDebouncedInput hook for better performance * feat: first pass, anthropic bedrock params * chore: paramEndpoints check for endpointType too * fix: maxTokens to use coerceNumber.optional(), * feat: extra chat model params * chore: reduce code repetition * refactor: improve preset title handling in SaveAsPresetDialog component * refactor: improve preset handling in HeaderOptions component * chore: improve typing, replace legacy dialog for SaveAsPresetDialog * feat: save as preset from parameters panel * fix: multi-search in select dropdown when using Option type * refactor: update default showDefault value to false in Dynamic components * feat: Bedrock presets settings * chore: config, fix agents schema, update config version * refactor: update AWS region variable name in bedrock options endpoint to BEDROCK_AWS_DEFAULT_REGION * refactor: update baseEndpointSchema in config.ts to include baseURL property * refactor: update createRun function to include req parameter and set streamRate based on provider * feat: availableRegions via config * refactor: remove unused demo agent controller file * WIP: title * Update @librechat/agents to version 1.5.0 * chore: addTitle.js to handle empty responseText * feat: support images and titles * feat: context token updates * Refactor BaseClient test to use expect.objectContaining * refactor: add model select, remove header options params, move side panel params below prompts * chore: update models list, catch title error * feat: model service for bedrock models (env) * chore: Remove verbose debug log in AgentClient class following stream * feat(bedrock): track token spend; fix: token rates, value key mapping for AWS models * refactor: handle streamRate in `handleLLMNewToken` callback * chore: AWS Bedrock example config in `.env.example` * refactor: Rename bedrockMeta to bedrockGeneral in settings.ts and use for AI21 and Amazon Bedrock providers * refactor: Update `.env.example` with AWS Bedrock model IDs URL and additional notes * feat: titleModel support for bedrock * refactor: Update `.env.example` with additional notes for AWS Bedrock model IDs
This commit is contained in:
parent
8c14360263
commit
d59b62174f
134 changed files with 3684 additions and 1213 deletions
|
|
@ -123,11 +123,6 @@ const AskController = async (req, res, next, initializeClient, addTitle) => {
|
|||
};
|
||||
|
||||
let response = await client.sendMessage(text, messageOptions);
|
||||
|
||||
if (overrideParentMessageId) {
|
||||
response.parentMessageId = overrideParentMessageId;
|
||||
}
|
||||
|
||||
response.endpoint = endpointOption.endpoint;
|
||||
|
||||
const { conversation = {} } = await client.responsePromise;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ async function endpointController(req, res) {
|
|||
};
|
||||
}
|
||||
|
||||
if (mergedConfig[EModelEndpoint.bedrock] && req.app.locals?.[EModelEndpoint.bedrock]) {
|
||||
const { availableRegions } = req.app.locals[EModelEndpoint.bedrock];
|
||||
mergedConfig[EModelEndpoint.bedrock] = {
|
||||
...mergedConfig[EModelEndpoint.bedrock],
|
||||
availableRegions,
|
||||
};
|
||||
}
|
||||
|
||||
const endpointsConfig = orderEndpointsConfig(mergedConfig);
|
||||
|
||||
await cache.set(CacheKeys.ENDPOINT_CONFIG, endpointsConfig);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
const { GraphEvents, ToolEndHandler, ChatModelStreamHandler } = require('@librechat/agents');
|
||||
|
||||
/** @typedef {import('@librechat/agents').Graph} Graph */
|
||||
/** @typedef {import('@librechat/agents').EventHandler} EventHandler */
|
||||
/** @typedef {import('@librechat/agents').ModelEndData} ModelEndData */
|
||||
/** @typedef {import('@librechat/agents').ChatModelStreamHandler} ChatModelStreamHandler */
|
||||
/** @typedef {import('@librechat/agents').ContentAggregatorResult['aggregateContent']} ContentAggregator */
|
||||
/** @typedef {import('@librechat/agents').GraphEvents} GraphEvents */
|
||||
|
||||
/**
|
||||
|
|
@ -18,18 +21,55 @@ const sendEvent = (res, event) => {
|
|||
res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`);
|
||||
};
|
||||
|
||||
class ModelEndHandler {
|
||||
/**
|
||||
* @param {Array<UsageMetadata>} collectedUsage
|
||||
*/
|
||||
constructor(collectedUsage) {
|
||||
if (!Array.isArray(collectedUsage)) {
|
||||
throw new Error('collectedUsage must be an array');
|
||||
}
|
||||
this.collectedUsage = collectedUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} event
|
||||
* @param {ModelEndData | undefined} data
|
||||
* @param {Record<string, unknown> | undefined} metadata
|
||||
* @param {Graph} graph
|
||||
* @returns
|
||||
*/
|
||||
handle(event, data, metadata, graph) {
|
||||
if (!graph || !metadata) {
|
||||
console.warn(`Graph or metadata not found in ${event} event`);
|
||||
return;
|
||||
}
|
||||
|
||||
const usage = data?.output?.usage_metadata;
|
||||
|
||||
if (usage) {
|
||||
this.collectedUsage.push(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default handlers for stream events.
|
||||
* @param {{ res?: ServerResponse }} options - The options object.
|
||||
* @param {Object} options - The options object.
|
||||
* @param {ServerResponse} options.res - The options object.
|
||||
* @param {ContentAggregator} options.aggregateContent - The options object.
|
||||
* @param {Array<UsageMetadata>} options.collectedUsage - The list of collected usage metadata.
|
||||
* @returns {Record<string, t.EventHandler>} The default handlers.
|
||||
* @throws {Error} If the request is not found.
|
||||
*/
|
||||
function getDefaultHandlers({ res }) {
|
||||
if (!res) {
|
||||
throw new Error('Request not found');
|
||||
function getDefaultHandlers({ res, aggregateContent, collectedUsage }) {
|
||||
if (!res || !aggregateContent) {
|
||||
throw new Error(
|
||||
`[getDefaultHandlers] Missing required options: res: ${!res}, aggregateContent: ${!aggregateContent}`,
|
||||
);
|
||||
}
|
||||
const handlers = {
|
||||
// [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
||||
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
||||
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
||||
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
||||
[GraphEvents.ON_RUN_STEP]: {
|
||||
|
|
@ -40,6 +80,7 @@ function getDefaultHandlers({ res }) {
|
|||
*/
|
||||
handle: (event, data) => {
|
||||
sendEvent(res, { event, data });
|
||||
aggregateContent({ event, data });
|
||||
},
|
||||
},
|
||||
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
||||
|
|
@ -50,6 +91,7 @@ function getDefaultHandlers({ res }) {
|
|||
*/
|
||||
handle: (event, data) => {
|
||||
sendEvent(res, { event, data });
|
||||
aggregateContent({ event, data });
|
||||
},
|
||||
},
|
||||
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
||||
|
|
@ -60,6 +102,7 @@ function getDefaultHandlers({ res }) {
|
|||
*/
|
||||
handle: (event, data) => {
|
||||
sendEvent(res, { event, data });
|
||||
aggregateContent({ event, data });
|
||||
},
|
||||
},
|
||||
[GraphEvents.ON_MESSAGE_DELTA]: {
|
||||
|
|
@ -70,6 +113,7 @@ function getDefaultHandlers({ res }) {
|
|||
*/
|
||||
handle: (event, data) => {
|
||||
sendEvent(res, { event, data });
|
||||
aggregateContent({ event, data });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@
|
|||
// validateVisionModel,
|
||||
// mapModelToAzureConfig,
|
||||
// } = require('librechat-data-provider');
|
||||
const { Callback } = require('@librechat/agents');
|
||||
const { Callback, createMetadataAggregator } = require('@librechat/agents');
|
||||
const {
|
||||
Constants,
|
||||
EModelEndpoint,
|
||||
bedrockOutputParser,
|
||||
providerEndpointMap,
|
||||
removeNullishValues,
|
||||
} = require('librechat-data-provider');
|
||||
|
|
@ -23,15 +25,27 @@ const {
|
|||
formatAgentMessages,
|
||||
createContextHandlers,
|
||||
} = require('~/app/clients/prompts');
|
||||
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
|
||||
const Tokenizer = require('~/server/services/Tokenizer');
|
||||
const { spendTokens } = require('~/models/spendTokens');
|
||||
const BaseClient = require('~/app/clients/BaseClient');
|
||||
// const { sleep } = require('~/server/utils');
|
||||
const { createRun } = require('./run');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/** @typedef {import('@librechat/agents').MessageContentComplex} MessageContentComplex */
|
||||
|
||||
// const providerSchemas = {
|
||||
// [EModelEndpoint.bedrock]: true,
|
||||
// };
|
||||
|
||||
const providerParsers = {
|
||||
[EModelEndpoint.bedrock]: bedrockOutputParser,
|
||||
};
|
||||
|
||||
class AgentClient extends BaseClient {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
super(null, options);
|
||||
|
||||
/** @type {'discard' | 'summarize'} */
|
||||
this.contextStrategy = 'discard';
|
||||
|
|
@ -39,11 +53,31 @@ class AgentClient extends BaseClient {
|
|||
/** @deprecated @type {true} - Is a Chat Completion Request */
|
||||
this.isChatCompletion = true;
|
||||
|
||||
const { maxContextTokens, modelOptions = {}, ...clientOptions } = options;
|
||||
/** @type {AgentRun} */
|
||||
this.run;
|
||||
|
||||
const {
|
||||
maxContextTokens,
|
||||
modelOptions = {},
|
||||
contentParts,
|
||||
collectedUsage,
|
||||
...clientOptions
|
||||
} = options;
|
||||
|
||||
this.modelOptions = modelOptions;
|
||||
this.maxContextTokens = maxContextTokens;
|
||||
this.options = Object.assign({ endpoint: EModelEndpoint.agents }, clientOptions);
|
||||
/** @type {MessageContentComplex[]} */
|
||||
this.contentParts = contentParts;
|
||||
/** @type {Array<UsageMetadata>} */
|
||||
this.collectedUsage = collectedUsage;
|
||||
this.options = Object.assign({ endpoint: options.endpoint }, clientOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aggregated content parts for the current run.
|
||||
* @returns {MessageContentComplex[]} */
|
||||
getContentParts() {
|
||||
return this.contentParts;
|
||||
}
|
||||
|
||||
setOptions(options) {
|
||||
|
|
@ -112,9 +146,27 @@ class AgentClient extends BaseClient {
|
|||
}
|
||||
|
||||
getSaveOptions() {
|
||||
const parseOptions = providerParsers[this.options.endpoint];
|
||||
let runOptions =
|
||||
this.options.endpoint === EModelEndpoint.agents
|
||||
? {
|
||||
model: undefined,
|
||||
// TODO:
|
||||
// would need to be override settings; otherwise, model needs to be undefined
|
||||
// model: this.override.model,
|
||||
// instructions: this.override.instructions,
|
||||
// additional_instructions: this.override.additional_instructions,
|
||||
}
|
||||
: {};
|
||||
|
||||
if (parseOptions) {
|
||||
runOptions = parseOptions(this.modelOptions);
|
||||
}
|
||||
|
||||
return removeNullishValues(
|
||||
Object.assign(
|
||||
{
|
||||
endpoint: this.options.endpoint,
|
||||
agent_id: this.options.agent.id,
|
||||
modelLabel: this.options.modelLabel,
|
||||
maxContextTokens: this.options.maxContextTokens,
|
||||
|
|
@ -122,15 +174,8 @@ class AgentClient extends BaseClient {
|
|||
imageDetail: this.options.imageDetail,
|
||||
spec: this.options.spec,
|
||||
},
|
||||
this.modelOptions,
|
||||
{
|
||||
model: undefined,
|
||||
// TODO:
|
||||
// would need to be override settings; otherwise, model needs to be undefined
|
||||
// model: this.override.model,
|
||||
// instructions: this.override.instructions,
|
||||
// additional_instructions: this.override.additional_instructions,
|
||||
},
|
||||
// TODO: PARSE OPTIONS BY PROVIDER, MAY CONTAIN SENSITIVE DATA
|
||||
runOptions,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -142,6 +187,16 @@ class AgentClient extends BaseClient {
|
|||
};
|
||||
}
|
||||
|
||||
async addImageURLs(message, attachments) {
|
||||
const { files, image_urls } = await encodeAndFormat(
|
||||
this.options.req,
|
||||
attachments,
|
||||
this.options.agent.provider,
|
||||
);
|
||||
message.image_urls = image_urls.length ? image_urls : undefined;
|
||||
return files;
|
||||
}
|
||||
|
||||
async buildMessages(
|
||||
messages,
|
||||
parentMessageId,
|
||||
|
|
@ -270,25 +325,34 @@ class AgentClient extends BaseClient {
|
|||
/** @type {sendCompletion} */
|
||||
async sendCompletion(payload, opts = {}) {
|
||||
this.modelOptions.user = this.user;
|
||||
return await this.chatCompletion({
|
||||
await this.chatCompletion({
|
||||
payload,
|
||||
onProgress: opts.onProgress,
|
||||
abortController: opts.abortController,
|
||||
});
|
||||
return this.contentParts;
|
||||
}
|
||||
|
||||
// async recordTokenUsage({ promptTokens, completionTokens, context = 'message' }) {
|
||||
// await spendTokens(
|
||||
// {
|
||||
// context,
|
||||
// model: this.modelOptions.model,
|
||||
// conversationId: this.conversationId,
|
||||
// user: this.user ?? this.options.req.user?.id,
|
||||
// endpointTokenConfig: this.options.endpointTokenConfig,
|
||||
// },
|
||||
// { promptTokens, completionTokens },
|
||||
// );
|
||||
// }
|
||||
/**
|
||||
* @param {Object} params
|
||||
* @param {string} [params.model]
|
||||
* @param {string} [params.context='message']
|
||||
* @param {UsageMetadata[]} [params.collectedUsage=this.collectedUsage]
|
||||
*/
|
||||
async recordCollectedUsage({ model, context = 'message', collectedUsage = this.collectedUsage }) {
|
||||
for (const usage of collectedUsage) {
|
||||
await spendTokens(
|
||||
{
|
||||
context,
|
||||
model: model ?? this.modelOptions.model,
|
||||
conversationId: this.conversationId,
|
||||
user: this.user ?? this.options.req.user?.id,
|
||||
endpointTokenConfig: this.options.endpointTokenConfig,
|
||||
},
|
||||
{ promptTokens: usage.input_tokens, completionTokens: usage.output_tokens },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async chatCompletion({ payload, abortController = null }) {
|
||||
try {
|
||||
|
|
@ -398,9 +462,8 @@ class AgentClient extends BaseClient {
|
|||
// });
|
||||
// }
|
||||
|
||||
// const streamRate = this.options.streamRate ?? Constants.DEFAULT_STREAM_RATE;
|
||||
|
||||
const run = await createRun({
|
||||
req: this.options.req,
|
||||
agent: this.options.agent,
|
||||
tools: this.options.tools,
|
||||
toolMap: this.options.toolMap,
|
||||
|
|
@ -415,6 +478,7 @@ class AgentClient extends BaseClient {
|
|||
thread_id: this.conversationId,
|
||||
},
|
||||
run_id: this.responseMessageId,
|
||||
signal: abortController.signal,
|
||||
streamMode: 'values',
|
||||
version: 'v2',
|
||||
};
|
||||
|
|
@ -423,8 +487,10 @@ class AgentClient extends BaseClient {
|
|||
throw new Error('Failed to create run');
|
||||
}
|
||||
|
||||
this.run = run;
|
||||
|
||||
const messages = formatAgentMessages(payload);
|
||||
const runMessages = await run.processStream({ messages }, config, {
|
||||
await run.processStream({ messages }, config, {
|
||||
[Callback.TOOL_ERROR]: (graph, error, toolId) => {
|
||||
logger.error(
|
||||
'[api/server/controllers/agents/client.js #chatCompletion] Tool Error',
|
||||
|
|
@ -433,14 +499,94 @@ class AgentClient extends BaseClient {
|
|||
);
|
||||
},
|
||||
});
|
||||
// console.dir(runMessages, { depth: null });
|
||||
return runMessages;
|
||||
this.recordCollectedUsage({ context: 'message' }).catch((err) => {
|
||||
logger.error(
|
||||
'[api/server/controllers/agents/client.js #chatCompletion] Error recording collected usage',
|
||||
err,
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
'[api/server/controllers/agents/client.js #chatCompletion] Unhandled error type',
|
||||
if (!abortController.signal.aborted) {
|
||||
logger.error(
|
||||
'[api/server/controllers/agents/client.js #sendCompletion] Unhandled error type',
|
||||
err,
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
'[api/server/controllers/agents/client.js #sendCompletion] Operation aborted',
|
||||
err,
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {string} params.text
|
||||
* @param {string} params.conversationId
|
||||
*/
|
||||
async titleConvo({ text }) {
|
||||
if (!this.run) {
|
||||
throw new Error('Run not initialized');
|
||||
}
|
||||
const { handleLLMEnd, collected: collectedMetadata } = createMetadataAggregator();
|
||||
const clientOptions = {};
|
||||
const providerConfig = this.options.req.app.locals[this.options.agent.provider];
|
||||
if (
|
||||
providerConfig &&
|
||||
providerConfig.titleModel &&
|
||||
providerConfig.titleModel !== Constants.CURRENT_MODEL
|
||||
) {
|
||||
clientOptions.model = providerConfig.titleModel;
|
||||
}
|
||||
try {
|
||||
const titleResult = await this.run.generateTitle({
|
||||
inputText: text,
|
||||
contentParts: this.contentParts,
|
||||
clientOptions,
|
||||
chainOptions: {
|
||||
callbacks: [
|
||||
{
|
||||
handleLLMEnd,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const collectedUsage = collectedMetadata.map((item) => {
|
||||
let input_tokens, output_tokens;
|
||||
|
||||
if (item.usage) {
|
||||
input_tokens = item.usage.input_tokens || item.usage.inputTokens;
|
||||
output_tokens = item.usage.output_tokens || item.usage.outputTokens;
|
||||
} else if (item.tokenUsage) {
|
||||
input_tokens = item.tokenUsage.promptTokens;
|
||||
output_tokens = item.tokenUsage.completionTokens;
|
||||
}
|
||||
|
||||
return {
|
||||
input_tokens: input_tokens,
|
||||
output_tokens: output_tokens,
|
||||
};
|
||||
});
|
||||
|
||||
this.recordCollectedUsage({
|
||||
model: clientOptions.model,
|
||||
context: 'title',
|
||||
collectedUsage,
|
||||
}).catch((err) => {
|
||||
logger.error(
|
||||
'[api/server/controllers/agents/client.js #titleConvo] Error recording collected usage',
|
||||
err,
|
||||
);
|
||||
});
|
||||
|
||||
return titleResult.title;
|
||||
} catch (err) {
|
||||
logger.error('[api/server/controllers/agents/client.js #titleConvo] Error', err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
// Import the necessary modules
|
||||
const path = require('path');
|
||||
const base = path.resolve(__dirname, '..', '..', '..', '..', 'api');
|
||||
console.log(base);
|
||||
//api/server/controllers/agents/demo.js
|
||||
require('module-alias')({ base });
|
||||
const connectDb = require('~/lib/db/connectDb');
|
||||
const AgentClient = require('./client');
|
||||
|
||||
// Define the user and message options
|
||||
const user = 'user123';
|
||||
const parentMessageId = 'pmid123';
|
||||
const conversationId = 'cid456';
|
||||
const maxContextTokens = 200000;
|
||||
const req = {
|
||||
user: { id: user },
|
||||
};
|
||||
const progressOptions = {
|
||||
res: {},
|
||||
};
|
||||
|
||||
// Define the message options
|
||||
const messageOptions = {
|
||||
user,
|
||||
parentMessageId,
|
||||
conversationId,
|
||||
progressOptions,
|
||||
};
|
||||
|
||||
async function main() {
|
||||
await connectDb();
|
||||
const client = new AgentClient({ req, maxContextTokens });
|
||||
|
||||
const text = 'Hello, this is a test message.';
|
||||
|
||||
try {
|
||||
let response = await client.sendMessage(text, messageOptions);
|
||||
console.log('Response:', response);
|
||||
} catch (error) {
|
||||
console.error('Error sending message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const { Constants, getResponseSender } = require('librechat-data-provider');
|
||||
const { Constants } = require('librechat-data-provider');
|
||||
const { createAbortController, handleAbortError } = require('~/server/middleware');
|
||||
const { sendMessage } = require('~/server/utils');
|
||||
const { saveMessage } = require('~/models');
|
||||
|
|
@ -9,22 +9,17 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
text,
|
||||
endpointOption,
|
||||
conversationId,
|
||||
modelDisplayLabel,
|
||||
parentMessageId = null,
|
||||
overrideParentMessageId = null,
|
||||
} = req.body;
|
||||
|
||||
let sender;
|
||||
let userMessage;
|
||||
let userMessagePromise;
|
||||
let promptTokens;
|
||||
let userMessageId;
|
||||
let responseMessageId;
|
||||
let userMessagePromise;
|
||||
|
||||
const sender = getResponseSender({
|
||||
...endpointOption,
|
||||
model: endpointOption.modelOptions.model,
|
||||
modelDisplayLabel,
|
||||
});
|
||||
const newConvo = !conversationId;
|
||||
const user = req.user.id;
|
||||
|
||||
|
|
@ -39,6 +34,8 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
responseMessageId = data[key];
|
||||
} else if (key === 'promptTokens') {
|
||||
promptTokens = data[key];
|
||||
} else if (key === 'sender') {
|
||||
sender = data[key];
|
||||
} else if (!conversationId && key === 'conversationId') {
|
||||
conversationId = data[key];
|
||||
}
|
||||
|
|
@ -46,6 +43,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
};
|
||||
|
||||
try {
|
||||
/** @type {{ client: TAgentClient }} */
|
||||
const { client } = await initializeClient({ req, res, endpointOption });
|
||||
|
||||
const getAbortData = () => ({
|
||||
|
|
@ -54,8 +52,8 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
promptTokens,
|
||||
conversationId,
|
||||
userMessagePromise,
|
||||
// text: getPartialText(),
|
||||
messageId: responseMessageId,
|
||||
content: client.getContentParts(),
|
||||
parentMessageId: overrideParentMessageId ?? userMessageId,
|
||||
});
|
||||
|
||||
|
|
@ -90,11 +88,6 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
};
|
||||
|
||||
let response = await client.sendMessage(text, messageOptions);
|
||||
|
||||
if (overrideParentMessageId) {
|
||||
response.parentMessageId = overrideParentMessageId;
|
||||
}
|
||||
|
||||
response.endpoint = endpointOption.endpoint;
|
||||
|
||||
const { conversation = {} } = await client.responsePromise;
|
||||
|
|
@ -103,7 +96,6 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
|
||||
if (client.options.attachments) {
|
||||
userMessage.files = client.options.attachments;
|
||||
conversation.model = endpointOption.modelOptions.model;
|
||||
delete userMessage.image_urls;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const { Run } = require('@librechat/agents');
|
||||
const { Run, Providers } = require('@librechat/agents');
|
||||
const { providerEndpointMap } = require('librechat-data-provider');
|
||||
|
||||
/**
|
||||
|
|
@ -14,11 +14,12 @@ const { providerEndpointMap } = require('librechat-data-provider');
|
|||
* Creates a new Run instance with custom handlers and configuration.
|
||||
*
|
||||
* @param {Object} options - The options for creating the Run instance.
|
||||
* @param {ServerRequest} [options.req] - The server request.
|
||||
* @param {string | undefined} [options.runId] - Optional run ID; otherwise, a new run ID will be generated.
|
||||
* @param {Agent} options.agent - The agent for this run.
|
||||
* @param {StructuredTool[] | undefined} [options.tools] - The tools to use in the run.
|
||||
* @param {Record<string, StructuredTool[]> | undefined} [options.toolMap] - The tool map for the run.
|
||||
* @param {Record<GraphEvents, EventHandler> | undefined} [options.customHandlers] - Custom event handlers.
|
||||
* @param {string | undefined} [options.runId] - Optional run ID; otherwise, a new run ID will be generated.
|
||||
* @param {ClientOptions} [options.modelOptions] - Optional model to use; if not provided, it will use the default from modelMap.
|
||||
* @param {boolean} [options.streaming=true] - Whether to use streaming.
|
||||
* @param {boolean} [options.streamUsage=true] - Whether to stream usage information.
|
||||
|
|
@ -43,15 +44,22 @@ async function createRun({
|
|||
modelOptions,
|
||||
);
|
||||
|
||||
const graphConfig = {
|
||||
runId,
|
||||
llmConfig,
|
||||
tools,
|
||||
toolMap,
|
||||
instructions: agent.instructions,
|
||||
additional_instructions: agent.additional_instructions,
|
||||
};
|
||||
|
||||
// TEMPORARY FOR TESTING
|
||||
if (agent.provider === Providers.ANTHROPIC) {
|
||||
graphConfig.streamBuffer = 2000;
|
||||
}
|
||||
|
||||
return Run.create({
|
||||
graphConfig: {
|
||||
runId,
|
||||
llmConfig,
|
||||
tools,
|
||||
toolMap,
|
||||
instructions: agent.instructions,
|
||||
additional_instructions: agent.additional_instructions,
|
||||
},
|
||||
graphConfig,
|
||||
customHandlers,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const { nanoid } = require('nanoid');
|
||||
const { FileContext } = require('librechat-data-provider');
|
||||
const { FileContext, Constants } = require('librechat-data-provider');
|
||||
const {
|
||||
getAgent,
|
||||
createAgent,
|
||||
|
|
@ -9,6 +9,8 @@ const {
|
|||
} = require('~/models/Agent');
|
||||
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
||||
const { uploadImageBuffer } = require('~/server/services/Files/process');
|
||||
const { getProjectByName } = require('~/models/Project');
|
||||
const { updateAgentProjects } = require('~/models/Agent');
|
||||
const { deleteFileByFilter } = require('~/models/File');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
|
|
@ -53,16 +55,35 @@ const createAgentHandler = async (req, res) => {
|
|||
* @param {object} req - Express Request
|
||||
* @param {object} req.params - Request params
|
||||
* @param {string} req.params.id - Agent identifier.
|
||||
* @returns {Agent} 200 - success response - application/json
|
||||
* @param {object} req.user - Authenticated user information
|
||||
* @param {string} req.user.id - User ID
|
||||
* @returns {Promise<Agent>} 200 - success response - application/json
|
||||
* @returns {Error} 404 - Agent not found
|
||||
*/
|
||||
const getAgentHandler = async (req, res) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
const agent = await getAgent({ id });
|
||||
const author = req.user.id;
|
||||
|
||||
let query = { id, author };
|
||||
|
||||
const globalProject = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, ['agentIds']);
|
||||
if (globalProject && (globalProject.agentIds?.length ?? 0) > 0) {
|
||||
query = {
|
||||
$or: [{ id, $in: globalProject.agentIds }, query],
|
||||
};
|
||||
}
|
||||
|
||||
const agent = await getAgent(query);
|
||||
|
||||
if (!agent) {
|
||||
return res.status(404).json({ error: 'Agent not found' });
|
||||
}
|
||||
|
||||
if (agent.author !== author) {
|
||||
delete agent.author;
|
||||
}
|
||||
|
||||
return res.status(200).json(agent);
|
||||
} catch (error) {
|
||||
logger.error('[/Agents/:id] Error retrieving agent', error);
|
||||
|
|
@ -82,7 +103,17 @@ const getAgentHandler = async (req, res) => {
|
|||
const updateAgentHandler = async (req, res) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
const updatedAgent = await updateAgent({ id, author: req.user.id }, req.body);
|
||||
const { projectIds, removeProjectIds, ...updateData } = req.body;
|
||||
|
||||
let updatedAgent;
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
updatedAgent = await updateAgent({ id, author: req.user.id }, updateData);
|
||||
}
|
||||
|
||||
if (projectIds || removeProjectIds) {
|
||||
updatedAgent = await updateAgentProjects(id, projectIds, removeProjectIds);
|
||||
}
|
||||
|
||||
return res.json(updatedAgent);
|
||||
} catch (error) {
|
||||
logger.error('[/Agents/:id] Error updating Agent', error);
|
||||
|
|
@ -119,13 +150,13 @@ const deleteAgentHandler = async (req, res) => {
|
|||
* @param {object} req - Express Request
|
||||
* @param {object} req.query - Request query
|
||||
* @param {string} [req.query.user] - The user ID of the agent's author.
|
||||
* @returns {AgentListResponse} 200 - success response - application/json
|
||||
* @returns {Promise<AgentListResponse>} 200 - success response - application/json
|
||||
*/
|
||||
const getListAgentsHandler = async (req, res) => {
|
||||
try {
|
||||
const { user } = req.query;
|
||||
const filter = user ? { author: user } : {};
|
||||
const data = await getListAgents(filter);
|
||||
const data = await getListAgents({
|
||||
author: req.user.id,
|
||||
});
|
||||
return res.json(data);
|
||||
} catch (error) {
|
||||
logger.error('[/Agents] Error listing Agents', error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue