TypeScript agent that translates text using OpenAI's API via LangChain and integrates with Nevermined's task system.
- Translator Agent using Nevermined's Payments API (TypeScript)
The Translator Agent is a TypeScript application that translates text using OpenAI's API via LangChain and integrates with Nevermined's Payments API to handle task management and execution. This agent listens for translation tasks assigned to it via Nevermined's AI protocol, processes them, and updates the task status accordingly.
- Node.js v14 or higher
- npm or yarn
- Nevermined API Key
- OpenAI API Key
- Git
-
Clone the repository
git clone https://github.com/nevermined-io/agent-translator-ts.git cd agent-translator-ts
-
Install the dependencies
npm install
-
Configure environment variables
-
Copy the
.env.example
file to.env
cp .env.example .env
-
Edit the
.env
file and add your API keys and configuration:NVM_API_KEY=your_nevermined_api_key OPENAI_API_KEY=your_openai_api_key NVM_ENVIRONMENT=staging # or 'production' as per your setup AGENT_DID=your_agent_did
-
-
Run the agent
npm start
This tutorial will guide you through the implementation of the Nevermined integration in the translator agent.
Ensure you have the following files in your project:
main.ts
: The main script where the agent logic resides.translator.ts
: Contains the translation logic using OpenAI's API via LangChain..env
: Stores your environment variables (API keys and configurations)..env.example
: An example of the.env
file structure.package.json
: Lists all the Node.js dependencies required for the project.
Install the necessary Node.js packages listed in package.json
. The key dependencies related to the agent are:
@nevermined-io/payments
: The TypeScript SDK for Nevermined's Payments API.@langchain/core
: For working with language models and chains.@langchain/openai
: OpenAI library wrapper for langchain.
npm install
Before integrating Nevermined into your agent, you need to configure some environment variables.
-
Nevermined API Key (
NVM_API_KEY
)- Generate your Nevermined API Key by logging into your account at nevermined.app.
- Navigate to your profile settings to create or retrieve your API key.
-
Agent DID (
AGENT_DID
)- The Agent Decentralized Identifier (DID) corresponds to the asset you create on the Nevermined platform.
- To obtain your Agent DID:
- Create a subscription plan and an asset on nevermined.app.
- The DID of the asset you created will serve as your Agent DID.
-
Other Environment Variables
- OpenAI API Key (
OPENAI_API_KEY
): Set this variable with your OpenAI API key. - Nevermined Environment (
NVM_ENVIRONMENT
): Specify the Nevermined environment you wish to connect to (staging
,production
, etc.).
- OpenAI API Key (
-
Setting Up the
.env
FileCreate a
.env
file in your project's root directory and add the following:NVM_API_KEY=your_nevermined_api_key OPENAI_API_KEY=your_openai_api_key NVM_ENVIRONMENT=staging # or 'production' as per your setup AGENT_DID=your_agent_did
This file will store your sensitive information securely and keep it out of your source code.
Integrating Nevermined into your agent involves several key steps: initializing the Payments client, subscribing to the AI protocol, handling incoming tasks, and updating task status and logging.
To interact with Nevermined's Payments API, you need to initialize a Payments
client in your agent's code. This client will handle authentication and provide methods to communicate with the Nevermined network.
import { Payments, EnvironmentName } from "@nevermined-io/payments";
import dotenv from "dotenv";
import pino from "pino";
// Load environment variables
dotenv.config();
// Retrieve environment variables
const NVM_ENVIRONMENT = process.env.NVM_ENVIRONMENT || "staging";
const NVM_API_KEY = process.env.NVM_API_KEY!;
const logger = pino({ level: "info" });
// Initialize the Payments instance
function initializePayments(nvmApiKey: string, environment: string) {
logger.info("Initializing Nevermined Payments Library...");
const paymentsInstance = Payments.getInstance({
nvmApiKey,
environment: environment as EnvironmentName,
});
if (!paymentsInstance.isLoggedIn) {
throw new Error("Failed to login to Nevermined Payments Library");
}
return paymentsInstance;
}
const payments = initializePayments(NVM_API_KEY, NVM_ENVIRONMENT);
logger.info(`Connected to Nevermined Network: ${NVM_ENVIRONMENT}`);
In this snippet:
nvmApiKey
: Your Nevermined API key from the environment variables.environment
: Specifies the Nevermined environment to connect to.initializePayments()
: Initializes the Payments client.
By initializing the Payments client, your agent is now authenticated and ready to interact with the Nevermined network.
Next, you need to subscribe your agent to the AI protocol to start receiving tasks assigned to it. This involves setting up an asynchronous listener that triggers whenever a new task is available.
import { AgentExecutionStatus } from "@nevermined-io/payments";
// Retrieve environment variables
const AGENT_DID = process.env.AGENT_DID!;
// Subscription options
const opts = {
joinAccountRoom: false,
joinAgentRooms: [AGENT_DID],
subscribeEventTypes: ["step-updated"],
getPendingEventsOnSubscribe: false,
};
/**
* The main function that initializes the agent and subscribes to the AI protocol.
*/
async function main() {
try {
// Initialize the Payments instance
const payments = initializePayments(NVM_API_KEY, NVM_ENVIRONMENT);
logger.info(`Connected to Nevermined Network: ${NVM_ENVIRONMENT}`);
// Create an instance of the Translator class
const translator = new Translator(OPENAI_API_KEY);
// Subscribe to the AI protocol to receive tasks assigned to this agent
await payments.query.subscribe(run, opts);
logger.info("Waiting for events!");
} catch (error) {
logger.error(`Error in main function: ${error}`);
payments.query.disconnect();
process.exit(1);
}
}
// Start the agent by calling the main function
main();
Here:
run
: The callback function in your agent that processes incoming tasks.joinAccountRoom
: Set tofalse
to focus on agent-specific events.joinAgentRooms
: A list containing your Agent DID to subscribe to tasks assigned to your agent.getPendingEventsOnSubscribe
: Iftrue
, retrieves any pending events upon subscription.
This subscription ensures your agent listens for and receives tasks from the Nevermined network.
When a task is assigned to your agent, the callback function you specified (run
) is invoked. This function should handle retrieving task details, processing the task, and updating its status.
First, retrieve the details of the task's current step using the step_id
provided in the incoming data.
async function run(data: any) {
try {
// Parse the incoming data
const eventData = JSON.parse(data);
logger.info(`Received event: ${JSON.stringify(eventData)}`);
// Retrieve the step information using the step_id from eventData
const step = await payments.query.getStep(eventData.step_id);
logger.info(
`Processing Step ${step.task_id} - ${step.step_id} [${step.step_status}]: ${step.input_query}`
);
// Check if the step status is pending; if not, skip processing
if (step.step_status !== AgentExecutionStatus.Pending) {
logger.warn(`Step ${step.step_id} is not pending. Skipping...`);
return;
}
data
: The data received from the subscription.payments.query.getStep()
: Fetches the details of the specified step, such as input data and status.
Extract the necessary input from the step and perform the required processing. For example, if your agent translates text, you would retrieve the text to translate:
// Log the initiation of the translation task
await logMessage({
task_id: step.task_id,
level: "info",
message: `Starting translation...`,
});
// Extract the input text that needs to be translated
const inputText = step.input_query;
// Perform the task using your agent's processing logic
const translatedText = await translator.translateText(inputText);
Replace translator.translateText
with the actual function or method your agent uses to process the task.
After processing the task, update the task's status and provide any output or results back to the Nevermined network.
// Update the step with the translated text and mark it as completed
const updateResult = await payments.query.updateStep(step.did, {
...step,
step_status: AgentExecutionStatus.Completed,
is_last: true,
output: translatedText,
cost: 5, // Adjust the cost as needed
});
// Log the completion of the translation task
if (updateResult.status === 201) {
await logMessage({
task_id: step.task_id,
message: "Translation completed.",
level: "info",
task_status: AgentExecutionStatus.Completed,
});
} else {
await logMessage({
task_id: step.task_id,
message: `Error updating step ${step.step_id} - ${JSON.stringify(
updateResult.data
)}`,
level: "error",
task_status: AgentExecutionStatus.Failed,
});
}
} catch (error) {
logger.error(`Error processing steps: ${error}`);
}
}
Here:
updateStep()
: Updates the step's status and output in the Nevermined network.step_status
: Set toCompleted
to indicate successful completion.output
: The result of your agent's processing.is_last
: Indicates whether this is the last step in the task.
Log the completion of the task for tracking and debugging purposes:
async function logMessage(logMessage: TaskLogMessage) {
// Log the message locally
if (logMessage.level === "error") logger.error(logMessage.message);
else if (logMessage.level === "warning") logger.warn(logMessage.message);
else if (logMessage.level === "debug") logger.debug(logMessage.message);
else logger.info(logMessage.message);
// Send the log message to Nevermined Payments API
await payments.query.logTask(logMessage);
}
In case of errors during processing, handle exceptions and update the task status accordingly:
} catch (e) {
// Handle any exceptions that occur during the translation
logger.error(`Error during translation: ${e}`);
await logMessage({
task_id: step.task_id,
message: `Error during translation: ${e}`,
level: "error",
task_status: AgentExecutionStatus.Failed,
});
}
Copyright 2024 Nevermined
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.