@promptbook/remote-server 0.89.0-5 โ†’ 0.89.0-7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.es.js CHANGED
@@ -3,6 +3,8 @@ import express from 'express';
3
3
  import http from 'http';
4
4
  import { Server } from 'socket.io';
5
5
  import spaceTrim$1, { spaceTrim } from 'spacetrim';
6
+ import swaggerJsdoc from 'swagger-jsdoc';
7
+ import swaggerUi from 'swagger-ui-express';
6
8
  import { forTime } from 'waitasecond';
7
9
  import { randomBytes } from 'crypto';
8
10
  import { spawn } from 'child_process';
@@ -31,7 +33,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
31
33
  * @generated
32
34
  * @see https://github.com/webgptorg/promptbook
33
35
  */
34
- const PROMPTBOOK_ENGINE_VERSION = '0.89.0-5';
36
+ const PROMPTBOOK_ENGINE_VERSION = '0.89.0-7';
35
37
  /**
36
38
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
37
39
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -156,6 +158,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
156
158
  */
157
159
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
158
160
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
161
+ // TODO: !!!!!! Just .promptbook dir, hardocode others
159
162
  /**
160
163
  * Where to store the temporary downloads
161
164
  *
@@ -207,9 +210,22 @@ const IS_PIPELINE_LOGIC_VALIDATED = just(
207
210
  true);
208
211
  /**
209
212
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
210
- * TODO: [๐Ÿง ][๐Ÿงœโ€โ™‚๏ธ] Maybe join remoteUrl and path into single value
213
+ * TODO: [๐Ÿง ][๐Ÿงœโ€โ™‚๏ธ] Maybe join remoteServerUrl and path into single value
211
214
  */
212
215
 
216
+ /**
217
+ * AuthenticationError is thrown from login function which is dependency of remote server
218
+ *
219
+ * @public exported from `@promptbook/core`
220
+ */
221
+ class AuthenticationError extends Error {
222
+ constructor(message) {
223
+ super(message);
224
+ this.name = 'AuthenticationError';
225
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
226
+ }
227
+ }
228
+
213
229
  /**
214
230
  * Generates random token
215
231
  *
@@ -543,6 +559,7 @@ const COMMON_JAVASCRIPT_ERRORS = {
543
559
  TypeError,
544
560
  URIError,
545
561
  AggregateError,
562
+ AuthenticationError,
546
563
  /*
547
564
  Note: Not widely supported
548
565
  > InternalError,
@@ -578,6 +595,10 @@ function serializeError(error) {
578
595
 
579
596
  Cannot serialize error with name "${name}"
580
597
 
598
+ Authors of Promptbook probably forgot to add this error into the list of errors:
599
+ https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
600
+
601
+
581
602
  ${block(stack || message)}
582
603
 
583
604
  `));
@@ -6711,11 +6732,12 @@ async function $provideScriptingForNode(options) {
6711
6732
  * @public exported from `@promptbook/remote-server`
6712
6733
  */
6713
6734
  function startRemoteServer(options) {
6714
- const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, } = {
6735
+ const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
6715
6736
  isAnonymousModeAllowed: false,
6716
6737
  isApplicationModeAllowed: false,
6717
6738
  collection: null,
6718
6739
  createLlmExecutionTools: null,
6740
+ login: null,
6719
6741
  ...options,
6720
6742
  };
6721
6743
  // <- TODO: [๐Ÿฆช] Some helper type to be able to use discriminant union types with destructuring
@@ -6782,9 +6804,38 @@ function startRemoteServer(options) {
6782
6804
  response.setHeader('X-Powered-By', 'Promptbook engine');
6783
6805
  next();
6784
6806
  });
6807
+ const swaggerOptions = {
6808
+ definition: {
6809
+ openapi: '3.0.0',
6810
+ info: {
6811
+ title: 'Promptbook Remote Server API',
6812
+ version: '1.0.0',
6813
+ description: 'API documentation for the Promptbook Remote Server',
6814
+ },
6815
+ servers: [
6816
+ {
6817
+ url: `http://localhost:${port}${rootPath}`,
6818
+ // <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
6819
+ },
6820
+ ],
6821
+ },
6822
+ apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
6823
+ };
6824
+ const swaggerSpec = swaggerJsdoc(swaggerOptions);
6825
+ app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
6785
6826
  const runningExecutionTasks = [];
6786
6827
  // <- TODO: [๐Ÿคฌ] Identify the users
6787
6828
  // TODO: [๐Ÿง ] Do here some garbage collection of finished tasks
6829
+ /**
6830
+ * @swagger
6831
+ * /:
6832
+ * get:
6833
+ * summary: Get server details
6834
+ * description: Returns details about the Promptbook server.
6835
+ * responses:
6836
+ * 200:
6837
+ * description: Server details in markdown format.
6838
+ */
6788
6839
  app.get(['/', rootPath], async (request, response) => {
6789
6840
  var _a;
6790
6841
  if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
@@ -6821,9 +6872,12 @@ function startRemoteServer(options) {
6821
6872
 
6822
6873
  ## Paths
6823
6874
 
6824
- ${block(app._router.stack
6825
- .map(({ route }) => (route === null || route === void 0 ? void 0 : route.path) || null)
6826
- .filter((path) => path !== null)
6875
+ ${block([
6876
+ ...app._router.stack
6877
+ .map(({ route }) => (route === null || route === void 0 ? void 0 : route.path) || null)
6878
+ .filter((path) => path !== null),
6879
+ '/api-docs',
6880
+ ]
6827
6881
  .map((path) => `- ${path}`)
6828
6882
  .join('\n'))}
6829
6883
 
@@ -6841,8 +6895,81 @@ function startRemoteServer(options) {
6841
6895
  https://github.com/webgptorg/promptbook
6842
6896
  `));
6843
6897
  });
6844
- // TODO: !!!!!! Add login route
6845
- app.get(`${rootPath}/books`, async (request, response) => {
6898
+ /**
6899
+ * @swagger
6900
+ *
6901
+ * /login:
6902
+ * post:
6903
+ * summary: Login to the server
6904
+ * description: Login to the server and get identification.
6905
+ * requestBody:
6906
+ * required: true
6907
+ * content:
6908
+ * application/json:
6909
+ * schema:
6910
+ * type: object
6911
+ * properties:
6912
+ * username:
6913
+ * type: string
6914
+ * password:
6915
+ * type: string
6916
+ * appId:
6917
+ * type: string
6918
+ * responses:
6919
+ * 200:
6920
+ * description: Successful login
6921
+ * content:
6922
+ * application/json:
6923
+ * schema:
6924
+ * type: object
6925
+ * properties:
6926
+ * identification:
6927
+ * type: object
6928
+ */
6929
+ app.post([`/login`, `${rootPath}/login`], async (request, response) => {
6930
+ if (!isApplicationModeAllowed || login === null) {
6931
+ response.status(400).send('Application mode is not allowed');
6932
+ return;
6933
+ }
6934
+ try {
6935
+ const username = request.body.username;
6936
+ const password = request.body.password;
6937
+ const appId = request.body.appId;
6938
+ const identification = await login({ username, password, appId });
6939
+ response.status(201).send({ identification });
6940
+ return;
6941
+ }
6942
+ catch (error) {
6943
+ if (!(error instanceof Error)) {
6944
+ throw error;
6945
+ }
6946
+ if (error instanceof AuthenticationError) {
6947
+ response.status(401).send({ error: serializeError(error) });
6948
+ }
6949
+ console.warn(`Login function thrown different error than AuthenticationError`, {
6950
+ error,
6951
+ serializedError: serializeError(error),
6952
+ });
6953
+ response.status(400).send({ error: serializeError(error) });
6954
+ }
6955
+ });
6956
+ /**
6957
+ * @swagger
6958
+ * /books:
6959
+ * get:
6960
+ * summary: List all books
6961
+ * description: Returns a list of all available books in the collection.
6962
+ * responses:
6963
+ * 200:
6964
+ * description: A list of books.
6965
+ * content:
6966
+ * application/json:
6967
+ * schema:
6968
+ * type: array
6969
+ * items:
6970
+ * type: string
6971
+ */
6972
+ app.get([`/books`, `${rootPath}/books`], async (request, response) => {
6846
6973
  if (collection === null) {
6847
6974
  response.status(500).send('No collection available');
6848
6975
  return;
@@ -6852,7 +6979,30 @@ function startRemoteServer(options) {
6852
6979
  response.send(pipelines);
6853
6980
  });
6854
6981
  // TODO: [๐Ÿง ] Is it secure / good idea to expose source codes of hosted books
6855
- app.get(`${rootPath}/books/*`, async (request, response) => {
6982
+ /**
6983
+ * @swagger
6984
+ * /books/{bookId}:
6985
+ * get:
6986
+ * summary: Get book content
6987
+ * description: Returns the content of a specific book.
6988
+ * parameters:
6989
+ * - in: path
6990
+ * name: bookId
6991
+ * required: true
6992
+ * schema:
6993
+ * type: string
6994
+ * description: The ID of the book to retrieve.
6995
+ * responses:
6996
+ * 200:
6997
+ * description: The content of the book.
6998
+ * content:
6999
+ * text/markdown:
7000
+ * schema:
7001
+ * type: string
7002
+ * 404:
7003
+ * description: Book not found.
7004
+ */
7005
+ app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
6856
7006
  try {
6857
7007
  if (collection === null) {
6858
7008
  response.status(500).send('No collection nor books available');
@@ -6906,10 +7056,26 @@ function startRemoteServer(options) {
6906
7056
  };
6907
7057
  }
6908
7058
  }
6909
- app.get(`${rootPath}/executions`, async (request, response) => {
7059
+ /**
7060
+ * @swagger
7061
+ * /executions:
7062
+ * get:
7063
+ * summary: List all executions
7064
+ * description: Returns a list of all running execution tasks.
7065
+ * responses:
7066
+ * 200:
7067
+ * description: A list of execution tasks.
7068
+ * content:
7069
+ * application/json:
7070
+ * schema:
7071
+ * type: array
7072
+ * items:
7073
+ * type: object
7074
+ */
7075
+ app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
6910
7076
  response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
6911
7077
  });
6912
- app.get(`${rootPath}/executions/last`, async (request, response) => {
7078
+ app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
6913
7079
  // TODO: [๐Ÿคฌ] Filter only for user
6914
7080
  if (runningExecutionTasks.length === 0) {
6915
7081
  response.status(404).send('No execution tasks found');
@@ -6918,7 +7084,7 @@ function startRemoteServer(options) {
6918
7084
  const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
6919
7085
  response.send(exportExecutionTask(lastExecutionTask, true));
6920
7086
  });
6921
- app.get(`${rootPath}/executions/:taskId`, async (request, response) => {
7087
+ app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
6922
7088
  const { taskId } = request.params;
6923
7089
  // TODO: [๐Ÿคฌ] Filter only for user
6924
7090
  const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
@@ -6930,7 +7096,36 @@ function startRemoteServer(options) {
6930
7096
  }
6931
7097
  response.send(exportExecutionTask(executionTask, true));
6932
7098
  });
6933
- app.post(`${rootPath}/executions/new`, async (request, response) => {
7099
+ /**
7100
+ * @swagger
7101
+ * /executions/new:
7102
+ * post:
7103
+ * summary: Start a new execution
7104
+ * description: Starts a new execution task for a given pipeline.
7105
+ * requestBody:
7106
+ * required: true
7107
+ * content:
7108
+ * application/json:
7109
+ * schema:
7110
+ * type: object
7111
+ * properties:
7112
+ * pipelineUrl:
7113
+ * type: string
7114
+ * inputParameters:
7115
+ * type: object
7116
+ * identification:
7117
+ * type: object
7118
+ * responses:
7119
+ * 200:
7120
+ * description: The newly created execution task.
7121
+ * content:
7122
+ * application/json:
7123
+ * schema:
7124
+ * type: object
7125
+ * 400:
7126
+ * description: Invalid input.
7127
+ */
7128
+ app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
6934
7129
  try {
6935
7130
  const { inputParameters, identification /* <- [๐Ÿคฌ] */ } = request.body;
6936
7131
  const pipelineUrl = request.body.pipelineUrl || request.body.book;