@alpic80/rivet-cli 1.24.0-aidon.5 → 1.24.0-aidon.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.
@@ -1,32 +1,5 @@
1
1
  import { plugins } from '@ironclad/rivet-core';
2
2
  const pluginConfigurations = [
3
- {
4
- envVar: 'CHROMADB_PLUGIN',
5
- importPath: 'rivet-plugin-chromadb',
6
- isBuiltIn: false,
7
- registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
8
- settings: {
9
- envVarPrefix: 'CHROMA',
10
- settingsKey: 'chroma',
11
- settingsStructure: {
12
- databaseUri: 'DATABASE_URI',
13
- },
14
- },
15
- },
16
- {
17
- envVar: 'GOOGLE_PLUGIN',
18
- isBuiltIn: true,
19
- registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.google),
20
- settings: {
21
- envVarPrefix: 'GOOGLE',
22
- settingsKey: 'google',
23
- settingsStructure: {
24
- googleProjectId: 'PROJECT_ID',
25
- googleRegion: 'REGION',
26
- googleApplicationCredentials: 'APPLICATION_CREDENTIALS',
27
- },
28
- },
29
- },
30
3
  {
31
4
  envVar: 'AIDON_PLUGIN',
32
5
  isBuiltIn: true,
@@ -45,6 +18,7 @@ const pluginConfigurations = [
45
18
  settingsKey: 'anthropic',
46
19
  settingsStructure: {
47
20
  anthropicApiKey: 'API_KEY',
21
+ anthropicApiEndpoint: 'API_ENDPOINT',
48
22
  },
49
23
  },
50
24
  },
@@ -70,35 +44,54 @@ const pluginConfigurations = [
70
44
  },
71
45
  },
72
46
  {
73
- envVar: 'HUGGINGFACE_PLUGIN',
47
+ envVar: 'CHROMA_PLUGIN',
48
+ importPath: 'rivet-plugin-chromadb',
49
+ isBuiltIn: false,
50
+ registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
51
+ settings: {
52
+ envVarPrefix: 'CHROMA',
53
+ settingsKey: 'chroma',
54
+ settingsStructure: {
55
+ databaseUri: 'DATABASE_URI',
56
+ },
57
+ },
58
+ },
59
+ {
60
+ envVar: 'GENTRACE_PLUGIN',
74
61
  isBuiltIn: true,
75
- registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.huggingFace),
62
+ registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.gentrace),
76
63
  settings: {
77
- envVarPrefix: 'HUGGINGFACE',
78
- settingsKey: 'huggingface',
64
+ envVarPrefix: 'GENTRACE',
65
+ settingsKey: 'gentrace',
79
66
  settingsStructure: {
80
- huggingFaceAccessToken: 'ACCESS_TOKEN',
67
+ gentraceApiKey: 'API_KEY',
81
68
  },
82
69
  },
83
70
  },
84
71
  {
85
- envVar: 'OPENAI_PLUGIN',
72
+ envVar: 'GOOGLE_PLUGIN',
86
73
  isBuiltIn: true,
87
- registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.openai),
74
+ registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.google),
88
75
  settings: {
89
- envVarPrefix: 'OPENAI',
90
- settingsKey: 'openai',
76
+ envVarPrefix: 'GOOGLE',
77
+ settingsKey: 'google',
78
+ settingsStructure: {
79
+ googleApiKey: 'API_KEY',
80
+ googleProjectId: 'PROJECT_ID',
81
+ googleRegion: 'REGION',
82
+ googleApplicationCredentials: 'APPLICATION_CREDENTIALS',
83
+ },
91
84
  },
92
85
  },
93
86
  {
94
- envVar: 'PINECONE_PLUGIN',
87
+ envVar: 'HUGGINGFACE_PLUGIN',
95
88
  isBuiltIn: true,
96
- registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.pinecone),
89
+ registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.huggingFace),
97
90
  settings: {
98
- envVarPrefix: 'PINECONE',
99
- settingsKey: 'pinecone',
91
+ envVarPrefix: 'HUGGINGFACE',
92
+ settingsKey: 'huggingface',
100
93
  settingsStructure: {
101
- pineconeApiKey: 'API_KEY',
94
+ huggingFaceAccessToken: 'ACCESS_TOKEN',
102
95
  },
103
96
  },
104
97
  },
@@ -128,6 +121,15 @@ const pluginConfigurations = [
128
121
  },
129
122
  },
130
123
  },
124
+ {
125
+ envVar: 'OPENAI_PLUGIN',
126
+ isBuiltIn: true,
127
+ registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.openai),
128
+ settings: {
129
+ envVarPrefix: 'OPENAI',
130
+ settingsKey: 'openai',
131
+ },
132
+ },
131
133
  {
132
134
  envVar: 'PDF2MD_PLUGIN',
133
135
  importPath: 'rivet-plugin-pdf2md',
@@ -138,6 +140,32 @@ const pluginConfigurations = [
138
140
  settingsKey: 'pdf2md',
139
141
  },
140
142
  },
143
+ {
144
+ envVar: 'PINECONE_PLUGIN',
145
+ isBuiltIn: true,
146
+ registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.pinecone),
147
+ settings: {
148
+ envVarPrefix: 'PINECONE',
149
+ settingsKey: 'pinecone',
150
+ settingsStructure: {
151
+ pineconeApiKey: 'API_KEY',
152
+ },
153
+ },
154
+ },
155
+ {
156
+ envVar: 'QDRANT_PLUGIN',
157
+ importPath: 'rivet-plugin-qdrant',
158
+ isBuiltIn: false,
159
+ registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
160
+ settings: {
161
+ envVarPrefix: 'QDRANT',
162
+ settingsKey: 'qdrant',
163
+ settingsStructure: {
164
+ qdrantApiKey: 'API_KEY',
165
+ qdrantUrl: 'URL',
166
+ },
167
+ },
168
+ },
141
169
  {
142
170
  envVar: 'TRANSFORMERLAB_PLUGIN',
143
171
  importPath: 'rivet-plugin-transformerlab',
@@ -151,6 +179,16 @@ const pluginConfigurations = [
151
179
  },
152
180
  },
153
181
  },
182
+ {
183
+ envVar: 'UTILITIES_PLUGIN',
184
+ importPath: 'rivet-utilities-plugin',
185
+ isBuiltIn: false,
186
+ registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
187
+ settings: {
188
+ envVarPrefix: 'UTILITIES',
189
+ settingsKey: 'utilities',
190
+ },
191
+ },
154
192
  ];
155
193
  const registeredPlugins = {};
156
194
  export async function setupPlugins(Rivet) {
@@ -1,6 +1,5 @@
1
1
  import { Hono } from 'hono';
2
2
  import { serve as serveHono } from '@hono/node-server';
3
- import { logger } from 'hono/logger';
4
3
  import { readdir, stat } from 'node:fs/promises';
5
4
  import * as path from 'path';
6
5
  import { extname, isAbsolute, join } from 'node:path';
@@ -10,8 +9,14 @@ import chalk from 'chalk';
10
9
  import { configDotenv } from 'dotenv';
11
10
  import * as Rivet from '@ironclad/rivet-node';
12
11
  import { setupPlugins, logAvailablePluginsInfo } from './pluginConfiguration.js';
12
+ import { combinedLogger } from '../lib/logger.js';
13
13
  export function makeCommand(y) {
14
14
  return y
15
+ .option('hostname', {
16
+ describe: 'The hostname to serve on',
17
+ type: 'string',
18
+ demandOption: false,
19
+ })
15
20
  .option('port', {
16
21
  describe: 'The port to serve on',
17
22
  type: 'number',
@@ -51,6 +56,11 @@ export function makeCommand(y) {
51
56
  describe: 'Expose the cost of the graph run in the response',
52
57
  type: 'boolean',
53
58
  default: false,
59
+ })
60
+ .option('expose-usage', {
61
+ describe: 'Expose the token usage of the graph run in the response',
62
+ type: 'boolean',
63
+ default: false,
54
64
  })
55
65
  .option('stream', {
56
66
  describe: 'Turns on streaming mode. Rivet events will be sent to the client using SSE (Server-Sent Events). If this is set to a Node ID or node title, only events for that node will be sent.',
@@ -83,16 +93,16 @@ export function makeCommand(y) {
83
93
  demandOption: false,
84
94
  })
85
95
  .positional('projectFile', {
86
- describe: 'The project file to serve. If omitted, the project file in the current directory is used. There cannot be multiple project files in the current directory.',
96
+ describe: 'The project file to serve. If omitted, the project file in the current directory is used. There cannot be multiple project files in the current directory unless projects-root-dir is set.',
87
97
  type: 'string',
88
98
  demandOption: false,
89
99
  });
90
100
  }
91
101
  const conditionalLogger = () => {
92
- const baseLogger = logger();
93
102
  return async (c, next) => {
94
103
  if (c.req.path !== '/health') {
95
- return baseLogger(c, next);
104
+ await combinedLogger(c, next);
105
+ return;
96
106
  }
97
107
  await next();
98
108
  };
@@ -103,6 +113,7 @@ export async function serve(cliArgs = {}) {
103
113
  debugger;
104
114
  const pluginSettings = await setupPlugins(Rivet);
105
115
  const args = {
116
+ hostname: cliArgs.hostname ?? process.env.HOSTNAME ?? '127.0.0.1',
106
117
  port: Number(cliArgs.port ?? process.env.PORT ?? 3000),
107
118
  projectFile: cliArgs.projectFile ?? process.env.PROJECT_FILE,
108
119
  dev: cliArgs.dev ?? process.env.NODE_ENV === 'development',
@@ -112,6 +123,7 @@ export async function serve(cliArgs = {}) {
112
123
  openaiEndpoint: cliArgs.openaiEndpoint ?? process.env.OPENAI_ENDPOINT,
113
124
  openaiOrganization: cliArgs.openaiOrganization ?? process.env.OPENAI_ORGANIZATION,
114
125
  exposeCost: cliArgs.exposeCost ?? process.env.EXPOSE_COST === 'true',
126
+ exposeUsage: cliArgs.exposeUsage ?? process.env.EXPOSE_USAGE === 'true',
115
127
  logRequests: cliArgs.logRequests ?? process.env.LOG_REQUESTS === 'true',
116
128
  logActivity: cliArgs.logActivity ?? process.env.LOG_ACTIVITY === 'true',
117
129
  logTrace: cliArgs.logTrace ?? process.env.LOG_TRACE === 'true',
@@ -138,11 +150,16 @@ export async function serve(cliArgs = {}) {
138
150
  throwIfNoMainGraph(initialProject, args.graph, projectFilePath);
139
151
  throwIfInvalidGraph(initialProject, args.graph);
140
152
  }
141
- if (args.stream != null) {
142
- console.log('Streaming is enabled');
143
- }
144
- if (args.streamNode != null) {
145
- console.log(`Streaming node ${chalk.bold(args.streamNode)}`);
153
+ if (args.logActivity) {
154
+ const logInfo = [
155
+ args.port && `Port:${chalk.bold.white(args.port)}`,
156
+ args.projectFile && `ProjectFile:${chalk.bold.white(args.projectFile)}`,
157
+ args.dev && `Dev:${chalk.bold.white(args.dev)}`,
158
+ args.graph && `Graph:${chalk.bold.white(args.graph)}`,
159
+ args.allowSpecifyingGraphId && `AllowSpecifyingGraphId:${chalk.bold.white(args.allowSpecifyingGraphId)}`,
160
+ args.logRequests && `LogRequests:${chalk.bold.white(args.logRequests)}`
161
+ ].filter(Boolean).join(', ');
162
+ console.log(chalk.green('Server config,', logInfo));
146
163
  }
147
164
  app.get('/health', async (c) => c.text('Healthy like Popeye'));
148
165
  app.post('/', async (c) => {
@@ -192,6 +209,7 @@ export async function serve(cliArgs = {}) {
192
209
  }
193
210
  const server = serveHono({
194
211
  port: args.port,
212
+ hostname: args.hostname,
195
213
  fetch: app.fetch,
196
214
  });
197
215
  if (args.projectsRootDir) {
@@ -233,19 +251,41 @@ export async function serve(cliArgs = {}) {
233
251
  }
234
252
  function createProcessGraph(ctx) {
235
253
  return async (opts) => {
254
+ let localCtx = { ...ctx };
236
255
  let inputs = {};
256
+ let runParams = {};
237
257
  if (opts.inputText.trim()) {
238
- inputs = JSON.parse(opts.inputText);
239
- if (typeof inputs !== 'object') {
258
+ const parsed = JSON.parse(opts.inputText);
259
+ if (typeof parsed !== 'object') {
240
260
  throw new Error('Inputs must be an object');
241
261
  }
262
+ if ('runParams' in parsed) {
263
+ runParams = parsed['runParams'];
264
+ delete parsed['runParams'];
265
+ localCtx = {
266
+ ...localCtx,
267
+ ...runParams
268
+ };
269
+ }
270
+ inputs = parsed;
271
+ }
272
+ if (localCtx.logActivity) {
273
+ const logInfo = [
274
+ `Input:${JSON.stringify(inputs)}`,
275
+ localCtx.stream && `Stream:${chalk.bold.white(localCtx.stream)}`,
276
+ localCtx.streamNode && `StreamNode:${chalk.bold.white(localCtx.streamNode)}`,
277
+ localCtx.exposeCost && `ExposeCost:${chalk.bold.white(localCtx.exposeCost)}`,
278
+ localCtx.exposeUsage && `ExposeUsage:${chalk.bold.white(localCtx.exposeUsage)}`,
279
+ localCtx.logTrace && `LogTrace:${chalk.bold.white(localCtx.logTrace)}`
280
+ ].filter(Boolean).join(', ');
281
+ console.log(chalk.green('Processing request,', logInfo));
242
282
  }
243
283
  const execOpts = {
244
- ...ctx,
284
+ ...localCtx,
245
285
  ...opts,
246
286
  inputs
247
287
  };
248
- if (ctx.streamNode != null) {
288
+ if (localCtx.stream != null) {
249
289
  const stream = await streamGraph(execOpts);
250
290
  return new Response(stream, {
251
291
  headers: {
@@ -263,45 +303,107 @@ function createProcessGraph(ctx) {
263
303
  });
264
304
  };
265
305
  }
266
- async function streamGraph({ project, inputs, graphId, openaiApiKey, openaiEndpoint, openaiOrganization, streamNode, }) {
306
+ const parseStream = (stream) => {
307
+ if (!stream.trim())
308
+ return true;
309
+ const start = [];
310
+ const finish = [];
311
+ const delta = [];
312
+ const pattern = /^([^[\]]+)(\[([SFD]+)\])?/;
313
+ for (const raw of stream.split(',')) {
314
+ const match = pattern.exec(raw);
315
+ if (!match)
316
+ continue;
317
+ const name = match[1]?.trim();
318
+ const flags = match[2];
319
+ if (!name)
320
+ continue;
321
+ if (!flags || flags.includes('S'))
322
+ start.push(name);
323
+ if (!flags || flags.includes('F'))
324
+ finish.push(name);
325
+ if (!flags || flags.includes('D'))
326
+ delta.push(name);
327
+ }
328
+ return { start, finish, delta };
329
+ };
330
+ async function streamGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logActivity, logTrace, stream, streamNode, }) {
267
331
  const { run, processor, getSSEStream } = createProcessor(project, {
268
332
  inputs,
269
333
  graph: graphId,
334
+ pluginSettings,
270
335
  openAiKey: openaiApiKey,
271
336
  openAiEndpoint: openaiEndpoint,
272
337
  openAiOrganization: openaiOrganization,
338
+ includeTrace: logTrace
273
339
  });
274
340
  if (streamNode) {
275
- const stream = getSingleNodeStream(processor, streamNode);
341
+ let sseStream;
342
+ if (exposeCost || exposeUsage) {
343
+ const baseOptions = {
344
+ exposeCost,
345
+ exposeUsage,
346
+ done: exposeCost || exposeUsage,
347
+ removeFinalOutput: exposeCost || exposeUsage,
348
+ error: logActivity
349
+ };
350
+ const spec = {
351
+ ...baseOptions,
352
+ partialOutputs: [streamNode],
353
+ nodeFinish: [streamNode],
354
+ };
355
+ sseStream = getSingleNodeStream(processor, spec);
356
+ }
357
+ else {
358
+ sseStream = getSingleNodeStream(processor, streamNode);
359
+ }
276
360
  run().catch((err) => {
277
361
  console.error(err);
278
362
  });
279
- return stream;
363
+ return sseStream;
280
364
  }
281
365
  else {
282
- const stream = getSSEStream({
283
- nodeStart: streamNode ? [streamNode] : true,
284
- nodeFinish: streamNode ? [streamNode] : true,
285
- partialOutputs: streamNode ? [streamNode] : true,
286
- });
366
+ const parsed = parseStream(stream);
367
+ const baseOptions = {
368
+ exposeCost,
369
+ exposeUsage,
370
+ done: exposeCost || exposeUsage,
371
+ removeFinalOutput: exposeCost || exposeUsage,
372
+ error: logActivity
373
+ };
374
+ const sseStream = getSSEStream(parsed === true
375
+ ? { ...baseOptions, nodeStart: true, nodeFinish: true, partialOutputs: true }
376
+ : {
377
+ ...baseOptions,
378
+ nodeStart: parsed.start,
379
+ nodeFinish: parsed.finish,
380
+ partialOutputs: parsed.delta
381
+ });
287
382
  run().catch((err) => {
288
383
  console.error(err);
289
384
  });
290
- return stream;
385
+ return sseStream;
291
386
  }
292
387
  }
293
- async function runGraph({ project, inputs, graphId, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, }) {
388
+ async function runGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logTrace }) {
294
389
  const { run } = createProcessor(project, {
295
390
  inputs,
296
391
  graph: graphId,
392
+ pluginSettings,
297
393
  openAiKey: openaiApiKey,
298
394
  openAiEndpoint: openaiEndpoint,
299
395
  openAiOrganization: openaiOrganization,
396
+ includeTrace: logTrace
300
397
  });
301
398
  const outputs = await run();
302
399
  if (!exposeCost) {
303
400
  delete outputs.cost;
304
401
  }
402
+ if (!exposeUsage) {
403
+ delete outputs.usage;
404
+ delete outputs.requestTokens;
405
+ delete outputs.responseTokens;
406
+ }
305
407
  return outputs;
306
408
  }
307
409
  function throwIfNoMainGraph(project, graph, projectFilePath) {
@@ -0,0 +1,20 @@
1
+ export const combinedLogger = async (c, next) => {
2
+ const start = Date.now();
3
+ await next();
4
+ const ms = Date.now() - start;
5
+ // ...rest as before
6
+ const req = c.req;
7
+ const res = c.res;
8
+ const remoteAddr = req.header('x-forwarded-for') ?? req.header('x-real-ip') ?? '::1';
9
+ const userAgent = req.header('user-agent') ?? '-';
10
+ const referer = req.header('referer') ?? '-';
11
+ const now = new Date();
12
+ const date = now.toISOString().replace('T', ' ').replace('Z', '');
13
+ const method = req.method;
14
+ const url = req.url;
15
+ const httpVersion = '1.1';
16
+ const status = res.status;
17
+ const contentLength = res.headers.get('content-length') ?? '-';
18
+ const log = `${remoteAddr} - - [${date}] "${method} ${url} HTTP/${httpVersion}" ${status} ${contentLength} "${referer}" "${userAgent}" ${ms}ms`;
19
+ console.log(log);
20
+ };
@@ -8,9 +8,9 @@ export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
8
8
  } & {
9
9
  "include-cost": boolean;
10
10
  } & {
11
- context: never[] | string[];
11
+ context: string[] | never[];
12
12
  } & {
13
- input: never[] | string[];
13
+ input: string[] | never[];
14
14
  }>;
15
15
  export declare function run(args: {
16
16
  projectFile: string;
@@ -1,5 +1,7 @@
1
1
  import type * as yargs from 'yargs';
2
2
  export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
3
+ hostname: string | undefined;
4
+ } & {
3
5
  port: number;
4
6
  } & {
5
7
  dev: boolean;
@@ -15,6 +17,8 @@ export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
15
17
  "openai-organization": string | undefined;
16
18
  } & {
17
19
  "expose-cost": boolean;
20
+ } & {
21
+ "expose-usage": boolean;
18
22
  } & {
19
23
  stream: string | undefined;
20
24
  } & {
@@ -31,6 +35,7 @@ export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
31
35
  projectFile: string | undefined;
32
36
  }>;
33
37
  type ServerContext = {
38
+ hostname: string | undefined;
34
39
  port: number;
35
40
  projectFile: string | undefined;
36
41
  dev: boolean;
@@ -40,6 +45,7 @@ type ServerContext = {
40
45
  openaiEndpoint: string | undefined;
41
46
  openaiOrganization: string | undefined;
42
47
  exposeCost: boolean;
48
+ exposeUsage: boolean;
43
49
  logRequests: boolean;
44
50
  logActivity: boolean;
45
51
  logTrace: boolean;
@@ -0,0 +1,2 @@
1
+ import type { Context, Next } from 'hono';
2
+ export declare const combinedLogger: (c: Context, next: Next) => Promise<void>;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@alpic80/rivet-cli",
3
3
  "license": "MIT",
4
4
  "repository": "https://github.com/castortech/rivet",
5
- "version": "1.24.0-aidon.5",
5
+ "version": "1.24.0-aidon.7",
6
6
  "src": "bin/cli.ts",
7
7
  "bin": {
8
8
  "rivet": "bin/cli.js"
@@ -24,10 +24,10 @@
24
24
  "dev:serve": "tsx src/cli.ts serve --projects-root-dir C:/temp/aidon/Rivet_Files"
25
25
  },
26
26
  "dependencies": {
27
- "@alpic80/rivet-core": "^1.24.0-aidon.3",
27
+ "@alpic80/rivet-core": "^1.24.0-aidon.4",
28
28
  "@alpic80/rivet-node": "^1.24.0-aidon.3",
29
29
  "@hono/node-server": "^1.13.8",
30
- "@ironclad/rivet-core": "npm:@alpic80/rivet-core@1.24.0-aidon.3",
30
+ "@ironclad/rivet-core": "npm:@alpic80/rivet-core@1.24.0-aidon.4",
31
31
  "@ironclad/rivet-node": "npm:@alpic80/rivet-node@1.24.0-aidon.3",
32
32
  "@types/dotenv": "^8.2.3",
33
33
  "chalk": "^5.4.1",