@alpic80/rivet-cli 1.24.0-aidon.5 → 1.24.0-aidon.6

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,6 +9,7 @@ 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
15
  .option('port', {
@@ -51,6 +51,11 @@ export function makeCommand(y) {
51
51
  describe: 'Expose the cost of the graph run in the response',
52
52
  type: 'boolean',
53
53
  default: false,
54
+ })
55
+ .option('expose-usage', {
56
+ describe: 'Expose the token usage of the graph run in the response',
57
+ type: 'boolean',
58
+ default: false,
54
59
  })
55
60
  .option('stream', {
56
61
  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 +88,16 @@ export function makeCommand(y) {
83
88
  demandOption: false,
84
89
  })
85
90
  .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.',
91
+ 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
92
  type: 'string',
88
93
  demandOption: false,
89
94
  });
90
95
  }
91
96
  const conditionalLogger = () => {
92
- const baseLogger = logger();
93
97
  return async (c, next) => {
94
98
  if (c.req.path !== '/health') {
95
- return baseLogger(c, next);
99
+ await combinedLogger(c, next);
100
+ return;
96
101
  }
97
102
  await next();
98
103
  };
@@ -112,6 +117,7 @@ export async function serve(cliArgs = {}) {
112
117
  openaiEndpoint: cliArgs.openaiEndpoint ?? process.env.OPENAI_ENDPOINT,
113
118
  openaiOrganization: cliArgs.openaiOrganization ?? process.env.OPENAI_ORGANIZATION,
114
119
  exposeCost: cliArgs.exposeCost ?? process.env.EXPOSE_COST === 'true',
120
+ exposeUsage: cliArgs.exposeUsage ?? process.env.EXPOSE_USAGE === 'true',
115
121
  logRequests: cliArgs.logRequests ?? process.env.LOG_REQUESTS === 'true',
116
122
  logActivity: cliArgs.logActivity ?? process.env.LOG_ACTIVITY === 'true',
117
123
  logTrace: cliArgs.logTrace ?? process.env.LOG_TRACE === 'true',
@@ -138,11 +144,16 @@ export async function serve(cliArgs = {}) {
138
144
  throwIfNoMainGraph(initialProject, args.graph, projectFilePath);
139
145
  throwIfInvalidGraph(initialProject, args.graph);
140
146
  }
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)}`);
147
+ if (args.logActivity) {
148
+ const logInfo = [
149
+ args.port && `Port:${chalk.bold.white(args.port)}`,
150
+ args.projectFile && `ProjectFile:${chalk.bold.white(args.projectFile)}`,
151
+ args.dev && `Dev:${chalk.bold.white(args.dev)}`,
152
+ args.graph && `Graph:${chalk.bold.white(args.graph)}`,
153
+ args.allowSpecifyingGraphId && `AllowSpecifyingGraphId:${chalk.bold.white(args.allowSpecifyingGraphId)}`,
154
+ args.logRequests && `LogRequests:${chalk.bold.white(args.logRequests)}`
155
+ ].filter(Boolean).join(', ');
156
+ console.log(chalk.green('Server config,', logInfo));
146
157
  }
147
158
  app.get('/health', async (c) => c.text('Healthy like Popeye'));
148
159
  app.post('/', async (c) => {
@@ -233,19 +244,41 @@ export async function serve(cliArgs = {}) {
233
244
  }
234
245
  function createProcessGraph(ctx) {
235
246
  return async (opts) => {
247
+ let localCtx = { ...ctx };
236
248
  let inputs = {};
249
+ let runParams = {};
237
250
  if (opts.inputText.trim()) {
238
- inputs = JSON.parse(opts.inputText);
239
- if (typeof inputs !== 'object') {
251
+ const parsed = JSON.parse(opts.inputText);
252
+ if (typeof parsed !== 'object') {
240
253
  throw new Error('Inputs must be an object');
241
254
  }
255
+ if ('runParams' in parsed) {
256
+ runParams = parsed['runParams'];
257
+ delete parsed['runParams'];
258
+ localCtx = {
259
+ ...localCtx,
260
+ ...runParams
261
+ };
262
+ }
263
+ inputs = parsed;
264
+ }
265
+ if (localCtx.logActivity) {
266
+ const logInfo = [
267
+ `Input:${JSON.stringify(inputs)}`,
268
+ localCtx.stream && `Stream:${chalk.bold.white(localCtx.stream)}`,
269
+ localCtx.streamNode && `StreamNode:${chalk.bold.white(localCtx.streamNode)}`,
270
+ localCtx.exposeCost && `ExposeCost:${chalk.bold.white(localCtx.exposeCost)}`,
271
+ localCtx.exposeUsage && `ExposeUsage:${chalk.bold.white(localCtx.exposeUsage)}`,
272
+ localCtx.logTrace && `LogTrace:${chalk.bold.white(localCtx.logTrace)}`
273
+ ].filter(Boolean).join(', ');
274
+ console.log(chalk.green('Processing request,', logInfo));
242
275
  }
243
276
  const execOpts = {
244
- ...ctx,
277
+ ...localCtx,
245
278
  ...opts,
246
279
  inputs
247
280
  };
248
- if (ctx.streamNode != null) {
281
+ if (localCtx.stream != null) {
249
282
  const stream = await streamGraph(execOpts);
250
283
  return new Response(stream, {
251
284
  headers: {
@@ -263,45 +296,107 @@ function createProcessGraph(ctx) {
263
296
  });
264
297
  };
265
298
  }
266
- async function streamGraph({ project, inputs, graphId, openaiApiKey, openaiEndpoint, openaiOrganization, streamNode, }) {
299
+ const parseStream = (stream) => {
300
+ if (!stream.trim())
301
+ return true;
302
+ const start = [];
303
+ const finish = [];
304
+ const delta = [];
305
+ const pattern = /^([^[\]]+)(\[([SFD]+)\])?/;
306
+ for (const raw of stream.split(',')) {
307
+ const match = pattern.exec(raw);
308
+ if (!match)
309
+ continue;
310
+ const name = match[1]?.trim();
311
+ const flags = match[2];
312
+ if (!name)
313
+ continue;
314
+ if (!flags || flags.includes('S'))
315
+ start.push(name);
316
+ if (!flags || flags.includes('F'))
317
+ finish.push(name);
318
+ if (!flags || flags.includes('D'))
319
+ delta.push(name);
320
+ }
321
+ return { start, finish, delta };
322
+ };
323
+ async function streamGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logActivity, logTrace, stream, streamNode, }) {
267
324
  const { run, processor, getSSEStream } = createProcessor(project, {
268
325
  inputs,
269
326
  graph: graphId,
327
+ pluginSettings,
270
328
  openAiKey: openaiApiKey,
271
329
  openAiEndpoint: openaiEndpoint,
272
330
  openAiOrganization: openaiOrganization,
331
+ includeTrace: logTrace
273
332
  });
274
333
  if (streamNode) {
275
- const stream = getSingleNodeStream(processor, streamNode);
334
+ let sseStream;
335
+ if (exposeCost || exposeUsage) {
336
+ const baseOptions = {
337
+ exposeCost,
338
+ exposeUsage,
339
+ done: exposeCost || exposeUsage,
340
+ removeFinalOutput: exposeCost || exposeUsage,
341
+ error: logActivity
342
+ };
343
+ const spec = {
344
+ ...baseOptions,
345
+ partialOutputs: [streamNode],
346
+ nodeFinish: [streamNode],
347
+ };
348
+ sseStream = getSingleNodeStream(processor, spec);
349
+ }
350
+ else {
351
+ sseStream = getSingleNodeStream(processor, streamNode);
352
+ }
276
353
  run().catch((err) => {
277
354
  console.error(err);
278
355
  });
279
- return stream;
356
+ return sseStream;
280
357
  }
281
358
  else {
282
- const stream = getSSEStream({
283
- nodeStart: streamNode ? [streamNode] : true,
284
- nodeFinish: streamNode ? [streamNode] : true,
285
- partialOutputs: streamNode ? [streamNode] : true,
286
- });
359
+ const parsed = parseStream(stream);
360
+ const baseOptions = {
361
+ exposeCost,
362
+ exposeUsage,
363
+ done: exposeCost || exposeUsage,
364
+ removeFinalOutput: exposeCost || exposeUsage,
365
+ error: logActivity
366
+ };
367
+ const sseStream = getSSEStream(parsed === true
368
+ ? { ...baseOptions, nodeStart: true, nodeFinish: true, partialOutputs: true }
369
+ : {
370
+ ...baseOptions,
371
+ nodeStart: parsed.start,
372
+ nodeFinish: parsed.finish,
373
+ partialOutputs: parsed.delta
374
+ });
287
375
  run().catch((err) => {
288
376
  console.error(err);
289
377
  });
290
- return stream;
378
+ return sseStream;
291
379
  }
292
380
  }
293
- async function runGraph({ project, inputs, graphId, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, }) {
381
+ async function runGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logTrace }) {
294
382
  const { run } = createProcessor(project, {
295
383
  inputs,
296
384
  graph: graphId,
385
+ pluginSettings,
297
386
  openAiKey: openaiApiKey,
298
387
  openAiEndpoint: openaiEndpoint,
299
388
  openAiOrganization: openaiOrganization,
389
+ includeTrace: logTrace
300
390
  });
301
391
  const outputs = await run();
302
392
  if (!exposeCost) {
303
393
  delete outputs.cost;
304
394
  }
395
+ if (!exposeUsage) {
396
+ delete outputs.usage;
397
+ delete outputs.requestTokens;
398
+ delete outputs.responseTokens;
399
+ }
305
400
  return outputs;
306
401
  }
307
402
  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;
@@ -15,6 +15,8 @@ export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
15
15
  "openai-organization": string | undefined;
16
16
  } & {
17
17
  "expose-cost": boolean;
18
+ } & {
19
+ "expose-usage": boolean;
18
20
  } & {
19
21
  stream: string | undefined;
20
22
  } & {
@@ -40,6 +42,7 @@ type ServerContext = {
40
42
  openaiEndpoint: string | undefined;
41
43
  openaiOrganization: string | undefined;
42
44
  exposeCost: boolean;
45
+ exposeUsage: boolean;
43
46
  logRequests: boolean;
44
47
  logActivity: boolean;
45
48
  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.6",
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",