diff --git a/package.json b/package.json index 9abd492..a96c43d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nevermined-io/payments", - "version": "0.7.5", + "version": "0.8.0", "description": "Typescript SDK to interact with the Nevermined Payments Protocol", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/api/nvm-backend.ts b/src/api/nvm-backend.ts index ae76708..0f9abff 100644 --- a/src/api/nvm-backend.ts +++ b/src/api/nvm-backend.ts @@ -88,7 +88,7 @@ export const DefaultSubscriptionOptions = { } export class NVMBackendApi { - private opts: BackendApiOptions + protected opts: BackendApiOptions private socketClient: any private userRoomId: string | undefined = undefined private taskCallbacks: Map = new Map() diff --git a/src/api/query-api.ts b/src/api/query-api.ts index 2c143a1..a517c10 100644 --- a/src/api/query-api.ts +++ b/src/api/query-api.ts @@ -61,6 +61,8 @@ export class AIQueryOptions { * This API is oriented for AI Builders providing AI Agents and AI Subscribers interacting with them. */ export class AIQueryApi extends NVMBackendApi { + queryOptionsCache = new Map() + constructor(opts: BackendApiOptions) { super(opts) } @@ -95,6 +97,43 @@ export class AIQueryApi extends NVMBackendApi { await super.connectTasksSocket(_callback, tasks, history) } + /** + * Get the required configuration for accessing a remote service agent. + * This configuration includes: + * - The JWT access token + * - The Proxy url that can be used to query the agent/service. + * + * @example + * ``` + * const accessConfig = await payments.query.getServiceAccessConfig(agentDID) + * console.log(`Agent JWT Token: ${accessConfig.accessToken}`) + * console.log(`Agent Proxy URL: ${accessConfig.neverminedProxyUri}`) + * ``` + * + * @param did - The DID of the agent/service. + * @returns A promise that resolves to the service token. + */ + public async getServiceAccessConfig(did: string): Promise<{ + accessToken: string + neverminedProxyUri: string + }> { + const options = { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.opts.apiKey}`, + }, + } + const url = new URL(`/api/v1/payments/service/token/${did}`, this.opts.backendHost) + const response = await fetch(url, options) + if (!response.ok) { + throw Error(`${response.statusText} - ${await response.text()}`) + } + + return (await response.json()).token + } + /** * Subscribers can create an AI Task for an Agent. The task must contain the input query that will be used by the AI Agent. * @see https://docs.nevermined.io/docs/protocol/query-protocol @@ -107,7 +146,7 @@ export class AIQueryApi extends NVMBackendApi { * * @example * ``` - * const accessConfig = await payments.getServiceAccessConfig(agentDID) + * const accessConfig = await payments.query.getServiceAccessConfig(agentDID) * const queryOpts = { * accessToken: accessConfig.accessToken, * proxyHost: accessConfig.neverminedProxyUri @@ -129,21 +168,26 @@ export class AIQueryApi extends NVMBackendApi { * * @param did - Agent DID * @param task - Task object. The task object should contain the query to execute and the name of the task. All the attributes here: @see https://docs.nevermined.io/docs/protocol/query-protocol#tasks-attributes - * @param queryOpts - The query options @see {@link Payments.getServiceAccessConfig} + * @param queryOpts - The query options @see {@link Payments.query.getServiceAccessConfig} * @param _callback - The callback to execute when a new task log event is received (optional) * @returns The result of the operation */ async createTask( did: string, task: CreateTaskDto, - queryOpts: AIQueryOptions, + queryOpts?: AIQueryOptions, _callback?: (err?: any) => any, ) { + if (!queryOpts || !queryOpts.accessToken) { + queryOpts = this.queryOptionsCache.has(did) + ? this.queryOptionsCache.get(did) + : await this.getServiceAccessConfig(did) + } const endpoint = TASK_ENDPOINT.replace('{did}', did) const reqOptions: HTTPRequestOptions = { sendThroughProxy: true, - ...(queryOpts.neverminedProxyUri && { proxyHost: queryOpts.neverminedProxyUri }), - ...(queryOpts.accessToken && { + ...(queryOpts?.neverminedProxyUri && { proxyHost: queryOpts.neverminedProxyUri }), + ...(queryOpts?.accessToken && { headers: { Authorization: `Bearer ${queryOpts.accessToken}` }, }), } @@ -165,7 +209,7 @@ export class AIQueryApi extends NVMBackendApi { * * @example * ``` - * const accessConfig = await payments.getServiceAccessConfig(agentDID) + * const accessConfig = await payments.query.getServiceAccessConfig(agentDID) * const queryOpts = { * accessToken: accessConfig.accessToken, * proxyHost: accessConfig.neverminedProxyUri @@ -182,11 +226,16 @@ export class AIQueryApi extends NVMBackendApi { * @param taskId - Task ID * @returns The task with the steps */ - async getTaskWithSteps(did: string, taskId: string, queryOpts: AIQueryOptions) { + async getTaskWithSteps(did: string, taskId: string, queryOpts?: AIQueryOptions) { + if (!queryOpts || !queryOpts.accessToken) { + queryOpts = this.queryOptionsCache.has(did) + ? this.queryOptionsCache.get(did) + : await this.getServiceAccessConfig(did) + } const reqOptions: HTTPRequestOptions = { sendThroughProxy: true, - ...(queryOpts.neverminedProxyUri && { proxyHost: queryOpts.neverminedProxyUri }), - ...(queryOpts.accessToken && { + ...(queryOpts?.neverminedProxyUri && { proxyHost: queryOpts.neverminedProxyUri }), + ...(queryOpts?.accessToken && { headers: { Authorization: `Bearer ${queryOpts.accessToken}` }, }), } diff --git a/src/payments.ts b/src/payments.ts index f2c5ffd..3a3b45b 100644 --- a/src/payments.ts +++ b/src/payments.ts @@ -1029,43 +1029,6 @@ export class Payments { return response.json() } - /** - * Get the required configuration for accessing a remote service agent. - * This configuration includes: - * - The JWT access token - * - The Proxy url that can be used to query the agent/service. - * - * @example - * ``` - * const accessConfig = await payments.getServiceAccessConfig(agentDID) - * console.log(`Agent JWT Token: ${accessConfig.accessToken}`) - * console.log(`Agent Proxy URL: ${accessConfig.neverminedProxyUri}`) - * ``` - * - * @param did - The DID of the agent/service. - * @returns A promise that resolves to the service token. - */ - public async getServiceAccessConfig(did: string): Promise<{ - accessToken: string - neverminedProxyUri: string - }> { - const options = { - method: 'GET', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.nvmApiKey}`, - }, - } - const url = new URL(`/api/v1/payments/service/token/${did}`, this.environment.backend) - const response = await fetch(url, options) - if (!response.ok) { - throw Error(`${response.statusText} - ${await response.text()}`) - } - - return (await response.json()).token - } - /** * Orders a Payment Plan. The user needs to have enough balance in the token selected by the owner of the Payment Plan. * diff --git a/tests/e2e/payments.e2e.test.ts b/tests/e2e/payments.e2e.test.ts index d7df8f1..1309b81 100644 --- a/tests/e2e/payments.e2e.test.ts +++ b/tests/e2e/payments.e2e.test.ts @@ -114,7 +114,7 @@ describe('Payments API (e2e)', () => { // "artifacts": [] // } - // const accessConfig = await paymentsSubscriber.getServiceAccessConfig(agentDID) + // const accessConfig = await paymentsSubscriber.query.getServiceAccessConfig(agentDID) // const queryOpts = { // accessToken: accessConfig.accessToken, // proxyHost: accessConfig.neverminedProxyUri @@ -229,7 +229,7 @@ describe('Payments API (e2e)', () => { additional_params: [], artifacts: [], } - subscriberQueryOpts = await paymentsSubscriber.getServiceAccessConfig(agentDID) + subscriberQueryOpts = await paymentsSubscriber.query.getServiceAccessConfig(agentDID) const taskResult = await paymentsSubscriber.query.createTask( agentDID, @@ -328,13 +328,13 @@ describe('Payments API (e2e)', () => { additional_params: [], artifacts: [], } - const accessConfig = await paymentsSubscriber.getServiceAccessConfig(agentDID) - const queryOpts = { - accessToken: accessConfig.accessToken, - proxyHost: accessConfig.neverminedProxyUri, - } + // const accessConfig = await paymentsSubscriber.query.getServiceAccessConfig(agentDID) + // const queryOpts = { + // accessToken: accessConfig.accessToken, + // proxyHost: accessConfig.neverminedProxyUri, + // } - const taskResult = await paymentsSubscriber.query.createTask(agentDID, aiTask, queryOpts) + const taskResult = await paymentsSubscriber.query.createTask(agentDID, aiTask) //, queryOpts) expect(taskResult).toBeDefined() expect(taskResult.status).toBe(201) @@ -358,14 +358,14 @@ describe('Payments API (e2e)', () => { additional_params: [], artifacts: [], } - const accessConfig = await paymentsSubscriber.getServiceAccessConfig(agentDID) - const queryOpts = { - accessToken: accessConfig.accessToken, - proxyHost: accessConfig.neverminedProxyUri, - } + // const accessConfig = await paymentsSubscriber.query.getServiceAccessConfig(agentDID) + // const queryOpts = { + // accessToken: accessConfig.accessToken, + // proxyHost: accessConfig.neverminedProxyUri, + // } let logsReceived = 0 - const taskResult = await paymentsSubscriber.query.createTask(agentDID, aiTask, queryOpts, async (data) => { + const taskResult = await paymentsSubscriber.query.createTask(agentDID, aiTask, undefined, async (data) => { console.log('New Task Log received', data) logsReceived++ })