@alpic80/rivet-cli 1.24.0-aidon.10
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/LICENSE +7 -0
- package/README.md +180 -0
- package/bin/cli.js +11 -0
- package/bin/commands/pluginConfiguration.js +274 -0
- package/bin/commands/run.js +98 -0
- package/bin/commands/serve.js +511 -0
- package/bin/lib/logger.js +20 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/commands/pluginConfiguration.d.ts +8 -0
- package/dist/types/commands/run.d.ts +22 -0
- package/dist/types/commands/serve.d.ts +58 -0
- package/dist/types/lib/logger.d.ts +2 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2023 Ironclad
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<h1 align="center"><img src="https://rivet.ironcladapp.com/img/logo-banner-wide.png" alt="Rivet Logo"></h1>
|
|
2
|
+
|
|
3
|
+
[](#contributors-)    [](https://discord.gg/qT8B2gv9Mg)
|
|
4
|
+
|
|
5
|
+
<h3 align="center"><a href="https://github.com/Ironclad/rivet/releases">Download</a> | <a href="https://rivet.ironcladapp.com">Website</a> | <a href="https://rivet.ironcladapp.com/docs">Documentation</a></h3>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://rivet.ironcladapp.com">Rivet</a>, the IDE for creating complex AI agents and prompt chaining, and embedding it in your application.
|
|
9
|
+
<br />
|
|
10
|
+
<br />
|
|
11
|
+
<a href="https://github.com/Ironclad/rivet/issues">Report Bug</a>
|
|
12
|
+
·
|
|
13
|
+
<a href="https://github.com/Ironclad/rivet/issues">Request Feature</a>
|
|
14
|
+
·
|
|
15
|
+
<a href="https://github.com/Ironclad/rivet/discussions">Discussions</a>
|
|
16
|
+
·
|
|
17
|
+
<a href="https://discord.gg/qT8B2gv9Mg">Chat</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
Have a question? Need some help? Check out the Rivet [Discord server](https://discord.gg/qT8B2gv9Mg)!
|
|
21
|
+
|
|
22
|
+
https://github.com/Ironclad/rivet/assets/448108/ad1d5e74-fe05-444e-8da7-66a1fc5b6848
|
|
23
|
+
|
|
24
|
+
- [About Rivet](#about-rivet)
|
|
25
|
+
- [Rivet Application](#rivet-application)
|
|
26
|
+
- [Rivet Core](#rivet-core)
|
|
27
|
+
- [Getting Started](#getting-started)
|
|
28
|
+
- [Prebuilt Binaries](#prebuilt-binaries)
|
|
29
|
+
- [Latest downloads](#latest-downloads)
|
|
30
|
+
- [All Releases](#all-releases)
|
|
31
|
+
- [Running from Source](#running-from-source)
|
|
32
|
+
- [Contributing](#contributing)
|
|
33
|
+
- [Code of Conduct](#code-of-conduct)
|
|
34
|
+
- [Troubleshooting](#troubleshooting)
|
|
35
|
+
- [Contributors ✨](#contributors-)
|
|
36
|
+
|
|
37
|
+
## About Rivet
|
|
38
|
+
|
|
39
|
+
### Rivet Application
|
|
40
|
+
|
|
41
|
+
Rivet is a desktop application for creating complex AI agents and prompt chaining, and embedding it in your application.
|
|
42
|
+
|
|
43
|
+
Rivet currently has LLM support for:
|
|
44
|
+
|
|
45
|
+
- [OpenAI GPT-3.5 and GPT-4](https://openai.com/gpt-4)
|
|
46
|
+
- [Anthropic Claude Instant and Claude 2](https://www.anthropic.com/index/claude-2)
|
|
47
|
+
- [Anthropic Claude 3 Haiku, Sonnet, and Opus] (https://www.anthropic.com/news/claude-3-family)
|
|
48
|
+
- [AssemblyAI LeMUR framework for voice data](https://www.assemblyai.com/products/speech-understanding?utm_source=rivet)
|
|
49
|
+
|
|
50
|
+
Rivet has embedding/vector database support for:
|
|
51
|
+
|
|
52
|
+
- [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings)
|
|
53
|
+
- [Pinecone](https://www.pinecone.io/)
|
|
54
|
+
|
|
55
|
+
Rivet also supports these additional integrations:
|
|
56
|
+
|
|
57
|
+
- [Speech-to-Text from AssemblyAI](https://www.assemblyai.com/discover/products/speech-to-text?utm_source=rivet)
|
|
58
|
+
|
|
59
|
+
For more information on how to use the application and all of its capabilities, see [the documentation](https://rivet.ironcladapp.com/docs)!
|
|
60
|
+
|
|
61
|
+
### Rivet Core
|
|
62
|
+
|
|
63
|
+
Rivet core is a TypeScript library for running graphs created in Rivet. It is used by the Rivet application, but can also be used in your own applications, so that Rivet can call into your own application's code, and your application can call into Rivet graphs.
|
|
64
|
+
|
|
65
|
+
For more information on using Rivet Core, see the [Rivet Integration Getting Started](https://rivet.ironcladapp.com/docs/api-reference/getting-started-integration) page and the related API documentation.
|
|
66
|
+
|
|
67
|
+
Rivet core is available on NPM as `@ironclad/rivet-core`. Rivet node is available as `@ironclad/rivet-node`. Documentation for each is available on the [Rivet website](https://rivet.ironcladapp.com/docs/api-reference).
|
|
68
|
+
|
|
69
|
+
## Getting Started
|
|
70
|
+
|
|
71
|
+
### Prebuilt Binaries
|
|
72
|
+
|
|
73
|
+
#### Latest downloads
|
|
74
|
+
|
|
75
|
+
- **[Download for MacOS](https://github.com/Ironclad/rivet/releases/latest/download/Rivet.dmg)**
|
|
76
|
+
- **[Download for Linux (AppImage)](https://github.com/Ironclad/rivet/releases/latest/download/Rivet.AppImage)**
|
|
77
|
+
- **[Download for Linux (dmg)](https://github.com/Ironclad/rivet/releases/latest/download/Rivet.dmg)**
|
|
78
|
+
- **[Download for Windows](https://github.com/Ironclad/rivet/releases/latest/download/Rivet-Setup.exe)**
|
|
79
|
+
|
|
80
|
+
#### All Releases
|
|
81
|
+
|
|
82
|
+
Check out the [releases page](https://github.com/Ironclad/rivet/releases) for all available releases.
|
|
83
|
+
|
|
84
|
+
### Running from Source
|
|
85
|
+
|
|
86
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for information on building and running Rivet from source.
|
|
87
|
+
|
|
88
|
+
## Contributing
|
|
89
|
+
|
|
90
|
+
All types of contributions are welcome - from code to documentation, bug reports, user experience feedback, and new feature suggestions!
|
|
91
|
+
|
|
92
|
+
Take a moment to read through the `CONTRIBUTING.md` file for help with setting up your development environment, and how to get started contributing to Rivet.
|
|
93
|
+
|
|
94
|
+
We use the All Contributors bot to recognize all our contributors, so every contribution is acknowledged. See the [Contributors](#contributors-) section below for everyone!
|
|
95
|
+
|
|
96
|
+
### Code of Conduct
|
|
97
|
+
|
|
98
|
+
The Rivet project is welcome to all contributors, and as such, we have a [Code of Conduct](./CODE_OF_CONDUCT.md) that all contributors must follow.
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
If you have run into any issues while running the Rivet application, or when integrating it into your code, please check the [Issues](https://github.com/Ironclad/rivet/issues) page for any existing issues, and if you can't find any, please open a new issue!
|
|
103
|
+
|
|
104
|
+
If you have any other questions on using Rivet, or have any other ideas, feel free to open a [discussion](https://github.com/Ironclad/rivet/discussions)!
|
|
105
|
+
|
|
106
|
+
## Contributors ✨
|
|
107
|
+
|
|
108
|
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
109
|
+
|
|
110
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
111
|
+
<!-- prettier-ignore-start -->
|
|
112
|
+
<!-- markdownlint-disable -->
|
|
113
|
+
<table>
|
|
114
|
+
<tbody>
|
|
115
|
+
<tr>
|
|
116
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abrenneke"><img src="https://avatars.githubusercontent.com/u/342540?v=4?s=100" width="100px;" alt="Andy Brenneke"/><br /><sub><b>Andy Brenneke</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=abrenneke" title="Code">💻</a> <a href="#ideas-abrenneke" title="Ideas, Planning, & Feedback">🤔</a> <a href="#research-abrenneke" title="Research">🔬</a> <a href="https://github.com/Ironclad/rivet/commits?author=abrenneke" title="Tests">⚠️</a> <a href="#tool-abrenneke" title="Tools">🔧</a> <a href="https://github.com/Ironclad/rivet/pulls?q=is%3Apr+reviewed-by%3Aabrenneke" title="Reviewed Pull Requests">👀</a> <a href="#question-abrenneke" title="Answering Questions">💬</a> <a href="#mentoring-abrenneke" title="Mentoring">🧑🏫</a></td>
|
|
117
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gogwilt"><img src="https://avatars.githubusercontent.com/u/448108?v=4?s=100" width="100px;" alt="Cai GoGwilt"/><br /><sub><b>Cai GoGwilt</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=gogwilt" title="Code">💻</a> <a href="#business-gogwilt" title="Business development">💼</a> <a href="#ideas-gogwilt" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-gogwilt" title="Maintenance">🚧</a> <a href="#promotion-gogwilt" title="Promotion">📣</a> <a href="https://github.com/Ironclad/rivet/pulls?q=is%3Apr+reviewed-by%3Agogwilt" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Agogwilt" title="Bug reports">🐛</a></td>
|
|
118
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/teddycoleman"><img src="https://avatars.githubusercontent.com/u/15386324?v=4?s=100" width="100px;" alt="Teddy Coleman"/><br /><sub><b>Teddy Coleman</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=teddycoleman" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Ateddycoleman" title="Bug reports">🐛</a></td>
|
|
119
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tberman"><img src="https://avatars.githubusercontent.com/u/183738?v=4?s=100" width="100px;" alt="Todd Berman"/><br /><sub><b>Todd Berman</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=tberman" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Atberman" title="Bug reports">🐛</a></td>
|
|
120
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/a-rothwell"><img src="https://avatars.githubusercontent.com/u/12634659?v=4?s=100" width="100px;" alt="Andrew Rothwell"/><br /><sub><b>Andrew Rothwell</b></sub></a><br /><a href="#tutorial-a-rothwell" title="Tutorials">✅</a> <a href="https://github.com/Ironclad/rivet/commits?author=a-rothwell" title="Documentation">📖</a> <a href="https://github.com/Ironclad/rivet/commits?author=a-rothwell" title="Code">💻</a></td>
|
|
121
|
+
<td align="center" valign="top" width="14.28%"><a href="http://twitter.com/dominiccooney"><img src="https://avatars.githubusercontent.com/u/55120?v=4?s=100" width="100px;" alt="Dominic Cooney"/><br /><sub><b>Dominic Cooney</b></sub></a><br /><a href="#ideas-dominiccooney" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Adominiccooney" title="Bug reports">🐛</a></td>
|
|
122
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ankrgyl"><img src="https://avatars.githubusercontent.com/u/565363?v=4?s=100" width="100px;" alt="Ankur Goyal"/><br /><sub><b>Ankur Goyal</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=ankrgyl" title="Documentation">📖</a> <a href="https://github.com/Ironclad/rivet/commits?author=ankrgyl" title="Code">💻</a></td>
|
|
123
|
+
</tr>
|
|
124
|
+
<tr>
|
|
125
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/scottbessler"><img src="https://avatars.githubusercontent.com/u/293802?v=4?s=100" width="100px;" alt="Scott Bessler"/><br /><sub><b>Scott Bessler</b></sub></a><br /><a href="#example-scottbessler" title="Examples">💡</a> <a href="https://github.com/Ironclad/rivet/commits?author=scottbessler" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Ascottbessler" title="Bug reports">🐛</a></td>
|
|
126
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/meeow"><img src="https://avatars.githubusercontent.com/u/18222559?v=4?s=100" width="100px;" alt="Brandon Hong"/><br /><sub><b>Brandon Hong</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=meeow" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Ameeow" title="Bug reports">🐛</a></td>
|
|
127
|
+
<td align="center" valign="top" width="14.28%"><a href="https://swimburger.net"><img src="https://avatars.githubusercontent.com/u/3382717?v=4?s=100" width="100px;" alt="Niels Swimberghe"/><br /><sub><b>Niels Swimberghe</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=Swimburger" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/commits?author=Swimburger" title="Documentation">📖</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3ASwimburger" title="Bug reports">🐛</a></td>
|
|
128
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tcgj"><img src="https://avatars.githubusercontent.com/u/7994529?v=4?s=100" width="100px;" alt="Terence C"/><br /><sub><b>Terence C</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=tcgj" title="Code">💻</a></td>
|
|
129
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tbrodahl-ironclad"><img src="https://avatars.githubusercontent.com/u/142630410?v=4?s=100" width="100px;" alt="Thomas Brodahl"/><br /><sub><b>Thomas Brodahl</b></sub></a><br /><a href="#design-tbrodahl-ironclad" title="Design">🎨</a></td>
|
|
130
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jkwok0714"><img src="https://avatars.githubusercontent.com/u/28716303?v=4?s=100" width="100px;" alt="Justin Kwok"/><br /><sub><b>Justin Kwok</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3AJkwok0714" title="Bug reports">🐛</a></td>
|
|
131
|
+
<td align="center" valign="top" width="14.28%"><a href="https://meebleforp.com/"><img src="https://avatars.githubusercontent.com/u/445650?v=4?s=100" width="100px;" alt="Zhang Yi Jiang"/><br /><sub><b>Zhang Yi Jiang</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=ZhangYiJiang" title="Documentation">📖</a></td>
|
|
132
|
+
</tr>
|
|
133
|
+
<tr>
|
|
134
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Crystalix007"><img src="https://avatars.githubusercontent.com/u/4603729?v=4?s=100" width="100px;" alt="Michael Kuc"/><br /><sub><b>Michael Kuc</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=Crystalix007" title="Code">💻</a></td>
|
|
135
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt="Ikko Eltociear Ashimine"/><br /><sub><b>Ikko Eltociear Ashimine</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=eltociear" title="Documentation">📖</a></td>
|
|
136
|
+
<td align="center" valign="top" width="14.28%"><a href="http://ohshutit.com"><img src="https://avatars.githubusercontent.com/u/54246375?v=4?s=100" width="100px;" alt="HU$H"/><br /><sub><b>HU$H</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=hushaudio" title="Code">💻</a> <a href="#ideas-hushaudio" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
137
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kcwhite"><img src="https://avatars.githubusercontent.com/u/3812801?v=4?s=100" width="100px;" alt="kcwhite"/><br /><sub><b>kcwhite</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=kcwhite" title="Documentation">📖</a></td>
|
|
138
|
+
<td align="center" valign="top" width="14.28%"><a href="https://gentrace.ai"><img src="https://avatars.githubusercontent.com/u/1203039?v=4?s=100" width="100px;" alt="Vivek Nair"/><br /><sub><b>Vivek Nair</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=viveknair" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/commits?author=viveknair" title="Documentation">📖</a> <a href="https://github.com/Ironclad/rivet/issues?q=author%3Aviveknair" title="Bug reports">🐛</a></td>
|
|
139
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CongYun22"><img src="https://avatars.githubusercontent.com/u/115971962?v=4?s=100" width="100px;" alt="丛云"/><br /><sub><b>丛云</b></sub></a><br /><a href="#ideas-CongYun22" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
140
|
+
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/vianneystroebel/"><img src="https://avatars.githubusercontent.com/u/628818?v=4?s=100" width="100px;" alt="Vianney Stroebel"/><br /><sub><b>Vianney Stroebel</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3Avibl" title="Bug reports">🐛</a></td>
|
|
141
|
+
</tr>
|
|
142
|
+
<tr>
|
|
143
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/advisely"><img src="https://avatars.githubusercontent.com/u/29242247?v=4?s=100" width="100px;" alt="HumanBot"/><br /><sub><b>HumanBot</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3Aadvisely" title="Bug reports">🐛</a></td>
|
|
144
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bradstallion"><img src="https://avatars.githubusercontent.com/u/37795833?v=4?s=100" width="100px;" alt="bradstallion"/><br /><sub><b>bradstallion</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3Abradstallion" title="Bug reports">🐛</a></td>
|
|
145
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wannabeasmartguy"><img src="https://avatars.githubusercontent.com/u/107250451?v=4?s=100" width="100px;" alt="Wannabeasmartguy"/><br /><sub><b>Wannabeasmartguy</b></sub></a><br /><a href="#ideas-Wannabeasmartguy" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
146
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mhamadeus"><img src="https://avatars.githubusercontent.com/u/77891233?v=4?s=100" width="100px;" alt="mhamadeus"/><br /><sub><b>mhamadeus</b></sub></a><br /><a href="#ideas-mhamadeus" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
147
|
+
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/moalturfi/"><img src="https://avatars.githubusercontent.com/u/71999644?v=4?s=100" width="100px;" alt="Mohamed Alturfi"/><br /><sub><b>Mohamed Alturfi</b></sub></a><br /><a href="#maintenance-altaiiiir" title="Maintenance">🚧</a></td>
|
|
148
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kojikeneda"><img src="https://avatars.githubusercontent.com/u/2552616?v=4?s=100" width="100px;" alt="kojikeneda"/><br /><sub><b>kojikeneda</b></sub></a><br /><a href="#ideas-kojikeneda" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
149
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Sandesh-Pyakurel"><img src="https://avatars.githubusercontent.com/u/82999440?v=4?s=100" width="100px;" alt="Sandesh Pyakurel"/><br /><sub><b>Sandesh Pyakurel</b></sub></a><br /><a href="#maintenance-Sandesh-Pyakurel" title="Maintenance">🚧</a></td>
|
|
150
|
+
</tr>
|
|
151
|
+
<tr>
|
|
152
|
+
<td align="center" valign="top" width="14.28%"><a href="http://www.linkedin.com/in/jasonlgill"><img src="https://avatars.githubusercontent.com/u/241711?v=4?s=100" width="100px;" alt="Jason Gill"/><br /><sub><b>Jason Gill</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3Ajasongill" title="Bug reports">🐛</a></td>
|
|
153
|
+
<td align="center" valign="top" width="14.28%"><a href="https://andriydruk.com"><img src="https://avatars.githubusercontent.com/u/1927559?v=4?s=100" width="100px;" alt="Andriy Druk"/><br /><sub><b>Andriy Druk</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/issues?q=author%3Aandriydruk" title="Bug reports">🐛</a></td>
|
|
154
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bardia-pourvakil"><img src="https://avatars.githubusercontent.com/u/132475166?v=4?s=100" width="100px;" alt="bardia-pourvakil"/><br /><sub><b>bardia-pourvakil</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=bardia-pourvakil" title="Documentation">📖</a></td>
|
|
155
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cinerar"><img src="https://avatars.githubusercontent.com/u/120550?v=4?s=100" width="100px;" alt="Cinerar"/><br /><sub><b>Cinerar</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=Cinerar" title="Documentation">📖</a></td>
|
|
156
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vpckso"><img src="https://avatars.githubusercontent.com/u/40294053?v=4?s=100" width="100px;" alt="Kit"/><br /><sub><b>Kit</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=vpckso" title="Code">💻</a></td>
|
|
157
|
+
<td align="center" valign="top" width="14.28%"><a href="https://reactgular.com"><img src="https://avatars.githubusercontent.com/u/50146659?v=4?s=100" width="100px;" alt="Nick Foscarini"/><br /><sub><b>Nick Foscarini</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=codemile" title="Code">💻</a> <a href="https://github.com/Ironclad/rivet/commits?author=codemile" title="Documentation">📖</a></td>
|
|
158
|
+
<td align="center" valign="top" width="14.28%"><a href="https://chang.com"><img src="https://avatars.githubusercontent.com/u/969364?v=4?s=100" width="100px;" alt="Wayne Chang"/><br /><sub><b>Wayne Chang</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=wayne-chang" title="Code">💻</a></td>
|
|
159
|
+
</tr>
|
|
160
|
+
<tr>
|
|
161
|
+
<td align="center" valign="top" width="14.28%"><a href="http://twitter.com/lou7s"><img src="https://avatars.githubusercontent.com/u/35469434?v=4?s=100" width="100px;" alt="loui7"/><br /><sub><b>loui7</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=loui7" title="Code">💻</a></td>
|
|
162
|
+
<td align="center" valign="top" width="14.28%"><a href="https://aschen.tech"><img src="https://avatars.githubusercontent.com/u/4447392?v=4?s=100" width="100px;" alt="Adrien Maret"/><br /><sub><b>Adrien Maret</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=Aschen" title="Code">💻</a></td>
|
|
163
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShravanSunder"><img src="https://avatars.githubusercontent.com/u/5294949?v=4?s=100" width="100px;" alt="Shravan Sunder"/><br /><sub><b>Shravan Sunder</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=ShravanSunder" title="Code">💻</a></td>
|
|
164
|
+
<td align="center" valign="top" width="14.28%"><a href="https://www.liaujianjie.com"><img src="https://avatars.githubusercontent.com/u/3143132?v=4?s=100" width="100px;" alt="Liau Jian Jie"/><br /><sub><b>Liau Jian Jie</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=liaujianjie" title="Code">💻</a></td>
|
|
165
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/d-debrock"><img src="https://avatars.githubusercontent.com/u/38040249?v=4?s=100" width="100px;" alt="d-debrock"/><br /><sub><b>d-debrock</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=d-debrock" title="Code">💻</a></td>
|
|
166
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cyppan"><img src="https://avatars.githubusercontent.com/u/1446201?v=4?s=100" width="100px;" alt="Cyprien Pannier"/><br /><sub><b>Cyprien Pannier</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=cyppan" title="Code">💻</a></td>
|
|
167
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/StephenGrider"><img src="https://avatars.githubusercontent.com/u/5003903?v=4?s=100" width="100px;" alt="Stephen Grider"/><br /><sub><b>Stephen Grider</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=StephenGrider" title="Code">💻</a> <a href="#ideas-StephenGrider" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-StephenGrider" title="Maintenance">🚧</a></td>
|
|
168
|
+
</tr>
|
|
169
|
+
<tr>
|
|
170
|
+
<td align="center" valign="top" width="14.28%"><a href="http://getenter.ai"><img src="https://avatars.githubusercontent.com/u/65794514?v=4?s=100" width="100px;" alt="Juliano Amadeu"/><br /><sub><b>Juliano Amadeu</b></sub></a><br /><a href="https://github.com/Ironclad/rivet/commits?author=julianolm" title="Code">💻</a> <a href="#ideas-julianolm" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
171
|
+
</tr>
|
|
172
|
+
</tbody>
|
|
173
|
+
</table>
|
|
174
|
+
|
|
175
|
+
<!-- markdownlint-restore -->
|
|
176
|
+
<!-- prettier-ignore-end -->
|
|
177
|
+
|
|
178
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
179
|
+
|
|
180
|
+
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import yargs from 'yargs';
|
|
3
|
+
import { hideBin } from 'yargs/helpers';
|
|
4
|
+
import { makeCommand as makeRunCommand, run } from './commands/run.js';
|
|
5
|
+
import { makeCommand as makeServeCommand, serve } from './commands/serve.js';
|
|
6
|
+
console.log('>>> CLI ENTRY REACHED');
|
|
7
|
+
await yargs(hideBin(process.argv))
|
|
8
|
+
.command('run <projectFile> [graphName]', 'Run a graph in a project file, or the main graph if graphName is not specified.', (y) => makeRunCommand(y), (args) => run(args))
|
|
9
|
+
.command('serve [projectFile]', 'Serve a project file as a REST API.', (y) => makeServeCommand(y), (args) => serve(args))
|
|
10
|
+
.demandCommand()
|
|
11
|
+
.parseAsync();
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { plugins } from '@ironclad/rivet-core';
|
|
2
|
+
const pluginConfigurations = [
|
|
3
|
+
{
|
|
4
|
+
envVar: 'AIDON_PLUGIN',
|
|
5
|
+
isBuiltIn: true,
|
|
6
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.aidon),
|
|
7
|
+
settings: {
|
|
8
|
+
envVarPrefix: 'AIDON',
|
|
9
|
+
settingsKey: 'aidon'
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
envVar: 'ANTHROPIC_PLUGIN',
|
|
14
|
+
isBuiltIn: true,
|
|
15
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.anthropic),
|
|
16
|
+
settings: {
|
|
17
|
+
envVarPrefix: 'ANTHROPIC',
|
|
18
|
+
settingsKey: 'anthropic',
|
|
19
|
+
settingsStructure: {
|
|
20
|
+
anthropicApiKey: 'API_KEY',
|
|
21
|
+
anthropicApiEndpoint: 'API_ENDPOINT',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
envVar: 'ASSEMBLYAI_PLUGIN',
|
|
27
|
+
isBuiltIn: true,
|
|
28
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.assemblyAi),
|
|
29
|
+
settings: {
|
|
30
|
+
envVarPrefix: 'ASSEMBLYAI',
|
|
31
|
+
settingsKey: 'assemblyAi',
|
|
32
|
+
settingsStructure: {
|
|
33
|
+
assemblyAiApiKey: 'API_KEY',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
envVar: 'AUTOEVALS_PLUGIN',
|
|
39
|
+
isBuiltIn: true,
|
|
40
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.autoevals),
|
|
41
|
+
settings: {
|
|
42
|
+
envVarPrefix: 'AUTOEVALS',
|
|
43
|
+
settingsKey: 'autoevals'
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
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',
|
|
61
|
+
isBuiltIn: true,
|
|
62
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.gentrace),
|
|
63
|
+
settings: {
|
|
64
|
+
envVarPrefix: 'GENTRACE',
|
|
65
|
+
settingsKey: 'gentrace',
|
|
66
|
+
settingsStructure: {
|
|
67
|
+
gentraceApiKey: 'API_KEY',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
envVar: 'GOOGLE_PLUGIN',
|
|
73
|
+
isBuiltIn: true,
|
|
74
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.google),
|
|
75
|
+
settings: {
|
|
76
|
+
envVarPrefix: 'GOOGLE',
|
|
77
|
+
settingsKey: 'google',
|
|
78
|
+
settingsStructure: {
|
|
79
|
+
googleApiKey: 'API_KEY',
|
|
80
|
+
googleProjectId: 'PROJECT_ID',
|
|
81
|
+
googleRegion: 'REGION',
|
|
82
|
+
googleApplicationCredentials: 'APPLICATION_CREDENTIALS',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
envVar: 'HUGGINGFACE_PLUGIN',
|
|
88
|
+
isBuiltIn: true,
|
|
89
|
+
registerFunction: (_plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugins.huggingFace),
|
|
90
|
+
settings: {
|
|
91
|
+
envVarPrefix: 'HUGGINGFACE',
|
|
92
|
+
settingsKey: 'huggingface',
|
|
93
|
+
settingsStructure: {
|
|
94
|
+
huggingFaceAccessToken: 'ACCESS_TOKEN',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
envVar: 'MONGODB_PLUGIN',
|
|
100
|
+
importPath: 'rivet-plugin-mongodb',
|
|
101
|
+
isBuiltIn: false,
|
|
102
|
+
registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
|
|
103
|
+
settings: {
|
|
104
|
+
envVarPrefix: 'MONGODB',
|
|
105
|
+
settingsKey: 'mongoDB',
|
|
106
|
+
settingsStructure: {
|
|
107
|
+
mongoDBConnectionString: 'CONNECTION_STRING',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
envVar: 'OLLAMA_PLUGIN',
|
|
113
|
+
importPath: 'rivet-plugin-ollama',
|
|
114
|
+
isBuiltIn: false,
|
|
115
|
+
registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
|
|
116
|
+
settings: {
|
|
117
|
+
envVarPrefix: 'OLLAMA',
|
|
118
|
+
settingsKey: 'ollama',
|
|
119
|
+
settingsStructure: {
|
|
120
|
+
host: 'HOST',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
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
|
+
},
|
|
133
|
+
{
|
|
134
|
+
envVar: 'PDF2MD_PLUGIN',
|
|
135
|
+
importPath: 'rivet-plugin-pdf2md',
|
|
136
|
+
isBuiltIn: false,
|
|
137
|
+
registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
|
|
138
|
+
settings: {
|
|
139
|
+
envVarPrefix: 'PDF2MD',
|
|
140
|
+
settingsKey: 'pdf2md',
|
|
141
|
+
},
|
|
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
|
+
},
|
|
169
|
+
{
|
|
170
|
+
envVar: 'TRANSFORMERLAB_PLUGIN',
|
|
171
|
+
importPath: 'rivet-plugin-transformerlab',
|
|
172
|
+
isBuiltIn: false,
|
|
173
|
+
registerFunction: (plugin, Rivet) => Rivet.globalRivetNodeRegistry.registerPlugin(plugin(Rivet)),
|
|
174
|
+
settings: {
|
|
175
|
+
envVarPrefix: 'TRANSFORMERLAB',
|
|
176
|
+
settingsKey: 'transformerlab',
|
|
177
|
+
settingsStructure: {
|
|
178
|
+
host: 'HOST',
|
|
179
|
+
},
|
|
180
|
+
},
|
|
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
|
+
},
|
|
192
|
+
];
|
|
193
|
+
const registeredPlugins = {};
|
|
194
|
+
export async function setupPlugins(Rivet) {
|
|
195
|
+
const pluginSettings = {};
|
|
196
|
+
console.log("Starting plugin registration...");
|
|
197
|
+
for (const config of pluginConfigurations) {
|
|
198
|
+
if (process.env[config.envVar] === 'true') {
|
|
199
|
+
// Skip registration if the plugin has already been registered
|
|
200
|
+
if (registeredPlugins[config.settings.settingsKey]) {
|
|
201
|
+
console.log(`${config.settings.settingsKey} plugin already registered.`);
|
|
202
|
+
}
|
|
203
|
+
let plugin = null;
|
|
204
|
+
if (!config.isBuiltIn) {
|
|
205
|
+
const module = await import(config.importPath);
|
|
206
|
+
plugin = module.default ?? module;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
// Perform registration if the plugin hasn't been registered yet
|
|
210
|
+
if (!registeredPlugins[config.settings.settingsKey]) {
|
|
211
|
+
config.registerFunction(plugin, Rivet);
|
|
212
|
+
console.log(`Successfully registered ${config.settings.settingsKey} plugin.`);
|
|
213
|
+
// Mark plugin as registered
|
|
214
|
+
registeredPlugins[config.settings.settingsKey] = true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
console.warn(`Failed to register ${config.settings.settingsKey} plugin: ${error.message}`);
|
|
219
|
+
}
|
|
220
|
+
// Prepare plugin-specific settings if needed
|
|
221
|
+
const pluginSpecificSettings = {};
|
|
222
|
+
const missingEnvVars = [];
|
|
223
|
+
if (config.settings?.settingsStructure) {
|
|
224
|
+
for (const [settingKey, envSuffix] of Object.entries(config.settings.settingsStructure)) {
|
|
225
|
+
// Construct the full environment variable name
|
|
226
|
+
const fullEnvName = `${config.settings.envVarPrefix}_${envSuffix}`;
|
|
227
|
+
// Fetch the value from the environment variables
|
|
228
|
+
const value = process.env[fullEnvName];
|
|
229
|
+
if (value !== undefined) {
|
|
230
|
+
pluginSpecificSettings[settingKey] = value;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
missingEnvVars.push(fullEnvName); // Add missing env var to the list
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (missingEnvVars.length > 0) {
|
|
237
|
+
// Log missing environment variables as a warning
|
|
238
|
+
console.warn(`[Warning] Missing environment variables for the '${config.settings.settingsKey}' \
|
|
239
|
+
plugin: ${missingEnvVars.join(', ')}.`);
|
|
240
|
+
}
|
|
241
|
+
// Assign the settings to the appropriate key in pluginSettings
|
|
242
|
+
if (Object.keys(pluginSpecificSettings).length > 0) {
|
|
243
|
+
pluginSettings[config.settings.settingsKey] = pluginSpecificSettings;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Optionally, log a summary or a positive confirmation message at the end
|
|
249
|
+
console.log("Plugin registration complete.");
|
|
250
|
+
return pluginSettings;
|
|
251
|
+
}
|
|
252
|
+
export function logAvailablePluginsInfo() {
|
|
253
|
+
console.log("Available Plugins and Required Environment Variables:");
|
|
254
|
+
console.log("-----------------------------------------------------");
|
|
255
|
+
pluginConfigurations.forEach(config => {
|
|
256
|
+
// Log the plugin's activation environment variable
|
|
257
|
+
console.log(`Plugin: ${config.settings.settingsKey}`);
|
|
258
|
+
if (process.env[config.envVar] === 'true') {
|
|
259
|
+
console.log(` Already marked for activation with env var: ${config.envVar} set to 'true'`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
console.log(` Activate with env var: ${config.envVar} (set to 'true' to enable)`);
|
|
263
|
+
}
|
|
264
|
+
// Check and log required environment variables for settings
|
|
265
|
+
if (config.settings?.settingsStructure) {
|
|
266
|
+
Object.entries(config.settings.settingsStructure).forEach(([settingKey, envSuffix]) => {
|
|
267
|
+
const fullEnvName = `${config.settings.envVarPrefix}_${envSuffix}`;
|
|
268
|
+
const valueExist = process.env[fullEnvName] !== undefined && process.env[fullEnvName] !== '';
|
|
269
|
+
console.log(` Required env var for ${settingKey}: ${fullEnvName} ${valueExist ? ' (has value)' : ''}`);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
console.log("-----------------------------------------------------");
|
|
273
|
+
});
|
|
274
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createProcessor, loadProjectFromFile } from '@alpic80/rivet-node';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
export function makeCommand(y) {
|
|
4
|
+
return y
|
|
5
|
+
.positional('projectFile', {
|
|
6
|
+
describe: 'The project file to run',
|
|
7
|
+
type: 'string',
|
|
8
|
+
demandOption: true,
|
|
9
|
+
})
|
|
10
|
+
.positional('graphName', {
|
|
11
|
+
describe: 'The name of the graph to run',
|
|
12
|
+
type: 'string',
|
|
13
|
+
})
|
|
14
|
+
.option('inputs-stdin', {
|
|
15
|
+
describe: 'Read inputs from stdin as JSON',
|
|
16
|
+
type: 'boolean',
|
|
17
|
+
default: false,
|
|
18
|
+
})
|
|
19
|
+
.option('include-cost', {
|
|
20
|
+
describe: 'Include the total cost in the output',
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
default: false,
|
|
23
|
+
})
|
|
24
|
+
.option('context', {
|
|
25
|
+
describe: 'Adds a context value to the graph run',
|
|
26
|
+
type: 'string',
|
|
27
|
+
array: true,
|
|
28
|
+
default: [],
|
|
29
|
+
})
|
|
30
|
+
.option('input', {
|
|
31
|
+
describe: 'Adds an input to the graph run',
|
|
32
|
+
type: 'string',
|
|
33
|
+
array: true,
|
|
34
|
+
default: [],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export async function run(args) {
|
|
38
|
+
try {
|
|
39
|
+
const projectPath = resolve(process.cwd(), args.projectFile);
|
|
40
|
+
const project = await loadProjectFromFile(projectPath);
|
|
41
|
+
if (!args.graphName && !project.metadata.mainGraphId) {
|
|
42
|
+
const validGraphs = Object.values(project.graphs).map((graph) => [graph.metadata.id, graph.metadata.name]);
|
|
43
|
+
const validGraphNames = validGraphs.map(([id, name]) => `• "${name}" (${id})`);
|
|
44
|
+
console.error(`No graph name provided, and project does not specify a main graph. Valid graphs are: \n${validGraphNames.join('\n')}\n\n Use either the graph's name or its ID. For example, \`rivet run my-project.rivet-project my-graph\` or \`rivet run my-project.rivet-project 1234abcd\``);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
let inputs = {};
|
|
48
|
+
if (args.inputsStdin) {
|
|
49
|
+
// Read json from stdin
|
|
50
|
+
const stdin = process.stdin;
|
|
51
|
+
stdin.setEncoding('utf8');
|
|
52
|
+
let input = '';
|
|
53
|
+
for await (const chunk of stdin) {
|
|
54
|
+
input += chunk;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
inputs = JSON.parse(input);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error('Failed to parse input JSON');
|
|
61
|
+
console.error(err);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
inputs = Object.fromEntries(args.input.map((input) => {
|
|
67
|
+
const [key, value] = input.split('=');
|
|
68
|
+
if (!key || !value) {
|
|
69
|
+
console.error(`Invalid input value: ${input}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
return [key, value];
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
const contextValues = Object.fromEntries(args.context.map((context) => {
|
|
76
|
+
const [key, value] = context.split('=');
|
|
77
|
+
if (!key || !value) {
|
|
78
|
+
console.error(`Invalid context value: ${context}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
return [key, value];
|
|
82
|
+
}));
|
|
83
|
+
const { run } = createProcessor(project, {
|
|
84
|
+
graph: args.graphName,
|
|
85
|
+
inputs,
|
|
86
|
+
context: contextValues,
|
|
87
|
+
});
|
|
88
|
+
const outputs = await run();
|
|
89
|
+
if (!args.includeCost) {
|
|
90
|
+
delete outputs.cost;
|
|
91
|
+
}
|
|
92
|
+
console.log(JSON.stringify(outputs, null, 2));
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error(err);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { serve as serveHono } from '@hono/node-server';
|
|
3
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { extname, isAbsolute, join } from 'node:path';
|
|
6
|
+
import didYouMean from 'didyoumean2';
|
|
7
|
+
import { createProcessor, loadProjectFromFile, getSingleNodeStream, } from '@alpic80/rivet-node';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { configDotenv } from 'dotenv';
|
|
10
|
+
import * as Rivet from '@ironclad/rivet-node';
|
|
11
|
+
import { setupPlugins, logAvailablePluginsInfo } from './pluginConfiguration.js';
|
|
12
|
+
import { combinedLogger } from '../lib/logger.js';
|
|
13
|
+
export function makeCommand(y) {
|
|
14
|
+
return y
|
|
15
|
+
.option('hostname', {
|
|
16
|
+
describe: 'The hostname to serve on',
|
|
17
|
+
type: 'string',
|
|
18
|
+
demandOption: false,
|
|
19
|
+
})
|
|
20
|
+
.option('port', {
|
|
21
|
+
describe: 'The port to serve on',
|
|
22
|
+
type: 'number',
|
|
23
|
+
default: 3000,
|
|
24
|
+
})
|
|
25
|
+
.option('dev', {
|
|
26
|
+
describe: 'Run in development mode: rereads the project file on each request',
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
default: false,
|
|
29
|
+
})
|
|
30
|
+
.option('graph', {
|
|
31
|
+
describe: 'The ID or name of the graph to run. If omitted, the main graph is used.',
|
|
32
|
+
type: 'string',
|
|
33
|
+
demandOption: false,
|
|
34
|
+
})
|
|
35
|
+
.option('allow-specifying-graph-id', {
|
|
36
|
+
describe: 'Allow specifying the graph ID in the URL path',
|
|
37
|
+
type: 'boolean',
|
|
38
|
+
default: false,
|
|
39
|
+
})
|
|
40
|
+
.option('openai-api-key', {
|
|
41
|
+
describe: 'The OpenAI API key to use for the project. If omitted, the environment variable OPENAI_API_KEY is used.',
|
|
42
|
+
type: 'string',
|
|
43
|
+
demandOption: false,
|
|
44
|
+
})
|
|
45
|
+
.option('openai-endpoint', {
|
|
46
|
+
describe: 'The OpenAI API endpoint to use for the project. If omitted, the environment variable OPENAI_ENDPOINT is used.',
|
|
47
|
+
type: 'string',
|
|
48
|
+
demandOption: false,
|
|
49
|
+
})
|
|
50
|
+
.option('openai-organization', {
|
|
51
|
+
describe: 'The OpenAI organization to use for the project. If omitted, the environment variable OPENAI_ORGANIZATION is used.',
|
|
52
|
+
type: 'string',
|
|
53
|
+
demandOption: false,
|
|
54
|
+
})
|
|
55
|
+
.option('expose-cost', {
|
|
56
|
+
describe: 'Expose the cost of the graph run in the response',
|
|
57
|
+
type: 'boolean',
|
|
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,
|
|
64
|
+
})
|
|
65
|
+
.option('stream', {
|
|
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.',
|
|
67
|
+
type: 'string',
|
|
68
|
+
demandOption: false,
|
|
69
|
+
})
|
|
70
|
+
.option('stream-node', {
|
|
71
|
+
describe: 'Streams the partial outputs of a specific node. Requires --stream to be set.',
|
|
72
|
+
type: 'string',
|
|
73
|
+
demandOption: false,
|
|
74
|
+
})
|
|
75
|
+
.option('log-requests', {
|
|
76
|
+
describe: 'Determines if all requests (except health checks) will be logged via Hono logger',
|
|
77
|
+
type: 'boolean',
|
|
78
|
+
default: false,
|
|
79
|
+
})
|
|
80
|
+
.option('log-activity', {
|
|
81
|
+
describe: 'Determines if basic activity should be logging during processing',
|
|
82
|
+
type: 'boolean',
|
|
83
|
+
default: false,
|
|
84
|
+
})
|
|
85
|
+
.option('log-trace', {
|
|
86
|
+
describe: 'Determines if includeTrace should be turned on during graph processing',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
default: false,
|
|
89
|
+
})
|
|
90
|
+
.option('projects-root-dir', {
|
|
91
|
+
describe: 'Specifies the root directory where project files are located. If specified, a projectFile argument will be a relative path to this directory',
|
|
92
|
+
type: 'string',
|
|
93
|
+
demandOption: false,
|
|
94
|
+
})
|
|
95
|
+
.positional('projectFile', {
|
|
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.',
|
|
97
|
+
type: 'string',
|
|
98
|
+
demandOption: false,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const conditionalLogger = () => {
|
|
102
|
+
return async (c, next) => {
|
|
103
|
+
if (c.req.path !== '/health') {
|
|
104
|
+
await combinedLogger(c, next);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await next();
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
export async function serve(cliArgs = {}) {
|
|
111
|
+
try {
|
|
112
|
+
configDotenv();
|
|
113
|
+
debugger;
|
|
114
|
+
const pluginSettings = await setupPlugins(Rivet);
|
|
115
|
+
const args = {
|
|
116
|
+
hostname: cliArgs.hostname ?? process.env.HOSTNAME ?? '127.0.0.1',
|
|
117
|
+
port: Number(cliArgs.port ?? process.env.PORT ?? 3000),
|
|
118
|
+
projectFile: cliArgs.projectFile ?? process.env.PROJECT_FILE,
|
|
119
|
+
dev: cliArgs.dev ?? process.env.NODE_ENV === 'development',
|
|
120
|
+
graph: cliArgs.graph ?? process.env.GRAPH,
|
|
121
|
+
allowSpecifyingGraphId: cliArgs.allowSpecifyingGraphId ?? (process.env.ALLOW_SPECIFYING_GRAPH_ID === 'true'),
|
|
122
|
+
openaiApiKey: cliArgs.openaiApiKey ?? process.env.OPENAI_API_KEY,
|
|
123
|
+
openaiEndpoint: cliArgs.openaiEndpoint ?? process.env.OPENAI_ENDPOINT,
|
|
124
|
+
openaiOrganization: cliArgs.openaiOrganization ?? process.env.OPENAI_ORGANIZATION,
|
|
125
|
+
exposeCost: cliArgs.exposeCost ?? (process.env.EXPOSE_COST === 'true'),
|
|
126
|
+
exposeUsage: cliArgs.exposeUsage ?? (process.env.EXPOSE_USAGE === 'true'),
|
|
127
|
+
logRequests: cliArgs.logRequests ?? (process.env.LOG_REQUESTS === 'true'),
|
|
128
|
+
logActivity: cliArgs.logActivity ?? (process.env.LOG_ACTIVITY === 'true'),
|
|
129
|
+
logTrace: cliArgs.logTrace ?? (process.env.LOG_TRACE === 'true'),
|
|
130
|
+
stream: cliArgs.stream ?? process.env.STREAM,
|
|
131
|
+
streamNode: cliArgs.streamNode ?? process.env.STREAM_NODE,
|
|
132
|
+
projectsRootDir: cliArgs.projectsRootDir ?? process.env.PROJECTS_ROOT_DIR,
|
|
133
|
+
pluginSettings: pluginSettings,
|
|
134
|
+
};
|
|
135
|
+
const processGraph = createProcessGraph(args);
|
|
136
|
+
const app = new Hono();
|
|
137
|
+
if (args.logRequests) {
|
|
138
|
+
app.use('*', conditionalLogger());
|
|
139
|
+
}
|
|
140
|
+
let projectFilePath = '';
|
|
141
|
+
let initialProject = null;
|
|
142
|
+
if (args.projectsRootDir) {
|
|
143
|
+
if (!(await validateProjectRootDirectory(args.projectsRootDir))) {
|
|
144
|
+
throwIfNoRootDirectory(args.projectsRootDir);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
projectFilePath = await getProjectFile(args.projectFile);
|
|
149
|
+
initialProject = await loadProjectFromFile(projectFilePath);
|
|
150
|
+
throwIfNoMainGraph(initialProject, args.graph, projectFilePath);
|
|
151
|
+
throwIfInvalidGraph(initialProject, args.graph);
|
|
152
|
+
}
|
|
153
|
+
console.log(chalk.green('Server running version 10'));
|
|
154
|
+
if (args.logActivity) {
|
|
155
|
+
const logInfo = [
|
|
156
|
+
args.hostname && `Hostname:${chalk.bold.white(args.hostname)}`,
|
|
157
|
+
args.port && `Port:${chalk.bold.white(args.port)}`,
|
|
158
|
+
args.projectFile && `ProjectFile:${chalk.bold.white(args.projectFile)}`,
|
|
159
|
+
args.dev && `Dev:${chalk.bold.white(args.dev)}`,
|
|
160
|
+
args.graph && `Graph:${chalk.bold.white(args.graph)}`,
|
|
161
|
+
args.allowSpecifyingGraphId && `AllowSpecifyingGraphId:${chalk.bold.white(args.allowSpecifyingGraphId)}`,
|
|
162
|
+
args.logRequests && `LogRequests:${chalk.bold.white(args.logRequests)}`
|
|
163
|
+
].filter(Boolean).join(', ');
|
|
164
|
+
console.log(chalk.green('Server config,', logInfo));
|
|
165
|
+
}
|
|
166
|
+
app.get('/health', async (c) => c.text('Healthy like Popeye'));
|
|
167
|
+
app.post('/', async (c) => {
|
|
168
|
+
if (args.projectsRootDir) {
|
|
169
|
+
return c.text('Configured with projects-root-dir which requires a project file to be specified in URL', 400);
|
|
170
|
+
}
|
|
171
|
+
const project = args.dev ? await loadProjectFromFile(projectFilePath) : initialProject;
|
|
172
|
+
const inputText = await c.req.text();
|
|
173
|
+
return await processGraph({ project, inputText });
|
|
174
|
+
});
|
|
175
|
+
if (args.allowSpecifyingGraphId || args.projectsRootDir) {
|
|
176
|
+
app.post('/*', async (c) => {
|
|
177
|
+
const full = decodeURIComponent(c.req.path.slice(1)); // remove leading slash
|
|
178
|
+
const parts = full.split('/');
|
|
179
|
+
let graphId;
|
|
180
|
+
let graphFile;
|
|
181
|
+
if (parts.length === 1) {
|
|
182
|
+
const single = parts[0]; //force TS to realize that there is content
|
|
183
|
+
if (single.endsWith('.rivet-project')) {
|
|
184
|
+
graphFile = single;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
graphId = single;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
graphId = parts.pop();
|
|
192
|
+
graphFile = parts.join('/');
|
|
193
|
+
}
|
|
194
|
+
let project;
|
|
195
|
+
if (args.projectsRootDir) {
|
|
196
|
+
if (!graphFile) {
|
|
197
|
+
return c.text('A graphFile is required when specifying projects-root-dir', 400);
|
|
198
|
+
}
|
|
199
|
+
const fileMissing = await testIfMissingFile(path.join(args.projectsRootDir, graphFile));
|
|
200
|
+
if (fileMissing) {
|
|
201
|
+
return c.text(`GraphFile ${graphFile} not found in root directory (${args.projectsRootDir})`, 400);
|
|
202
|
+
}
|
|
203
|
+
project = await loadProjectFromFile(path.join(args.projectsRootDir, graphFile));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
project = args.dev || args.projectsRootDir ? await loadProjectFromFile(projectFilePath) : initialProject;
|
|
207
|
+
}
|
|
208
|
+
const inputText = await c.req.text();
|
|
209
|
+
return await processGraph({ project, inputText, graphId });
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
const server = serveHono({
|
|
213
|
+
port: args.port,
|
|
214
|
+
hostname: args.hostname,
|
|
215
|
+
fetch: app.fetch,
|
|
216
|
+
});
|
|
217
|
+
if (args.projectsRootDir) {
|
|
218
|
+
console.log(chalk.green(`Serving specified project files from ${chalk.bold.white(args.projectsRootDir)} on port ${chalk.bold.white(args.port)}.`));
|
|
219
|
+
}
|
|
220
|
+
else if (initialProject) {
|
|
221
|
+
let servedGraphName;
|
|
222
|
+
if (args.graph) {
|
|
223
|
+
const graph = Object.values(initialProject.graphs).find((g) => g.metadata.id === args.graph || g.metadata.name === args.graph);
|
|
224
|
+
servedGraphName = graph.metadata.name;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const graph = Object.values(initialProject.graphs).find((g) => g.metadata.id === initialProject.metadata.mainGraphId);
|
|
228
|
+
servedGraphName = graph.metadata.name;
|
|
229
|
+
}
|
|
230
|
+
console.log(chalk.green(`Serving project file ${chalk.bold.white(projectFilePath)} on port ${chalk.bold.white(args.port)}.\nServing graph "${chalk.bold.white(servedGraphName)}".`));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log(chalk.red(`Misconfiguration, not serving from directory and have no initialProject`));
|
|
234
|
+
}
|
|
235
|
+
logAvailablePluginsInfo();
|
|
236
|
+
function shutdown() {
|
|
237
|
+
console.log('Shutting down...');
|
|
238
|
+
server.close((err) => {
|
|
239
|
+
if (err) {
|
|
240
|
+
console.error(err);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
process.exit(0);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
process.on('SIGINT', shutdown);
|
|
247
|
+
process.on('SIGTERM', shutdown);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
console.error(chalk.red(err));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function createProcessGraph(ctx) {
|
|
255
|
+
return async (opts) => {
|
|
256
|
+
let localCtx = { ...ctx };
|
|
257
|
+
let inputs = {};
|
|
258
|
+
let runParams = {};
|
|
259
|
+
if (opts.inputText.trim()) {
|
|
260
|
+
const parsed = JSON.parse(opts.inputText);
|
|
261
|
+
if (typeof parsed !== 'object') {
|
|
262
|
+
throw new Error('Inputs must be an object');
|
|
263
|
+
}
|
|
264
|
+
if ('runParams' in parsed) {
|
|
265
|
+
runParams = parsed['runParams'];
|
|
266
|
+
delete parsed['runParams'];
|
|
267
|
+
localCtx = {
|
|
268
|
+
...localCtx,
|
|
269
|
+
...runParams
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
inputs = parsed;
|
|
273
|
+
}
|
|
274
|
+
if (localCtx.logActivity) {
|
|
275
|
+
const logInfo = [
|
|
276
|
+
`Input:${JSON.stringify(inputs)}`,
|
|
277
|
+
localCtx.stream && `Stream:${chalk.bold.white(localCtx.stream)}`,
|
|
278
|
+
localCtx.streamNode && `StreamNode:${chalk.bold.white(localCtx.streamNode)}`,
|
|
279
|
+
localCtx.exposeCost && `ExposeCost:${chalk.bold.white(localCtx.exposeCost)}`,
|
|
280
|
+
localCtx.exposeUsage && `ExposeUsage:${chalk.bold.white(localCtx.exposeUsage)}`,
|
|
281
|
+
localCtx.logTrace && `LogTrace:${chalk.bold.white(localCtx.logTrace)}`
|
|
282
|
+
].filter(Boolean).join(', ');
|
|
283
|
+
console.log(chalk.green('Processing request,', logInfo));
|
|
284
|
+
}
|
|
285
|
+
const execOpts = {
|
|
286
|
+
...localCtx,
|
|
287
|
+
...opts,
|
|
288
|
+
inputs
|
|
289
|
+
};
|
|
290
|
+
if (localCtx.stream != null) {
|
|
291
|
+
const stream = await streamGraph(execOpts);
|
|
292
|
+
return new Response(stream, {
|
|
293
|
+
headers: {
|
|
294
|
+
'Content-Type': 'text/event-stream',
|
|
295
|
+
'Cache-Control': 'no-cache',
|
|
296
|
+
Connection: 'keep-alive'
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
const outputs = await runGraph(execOpts);
|
|
301
|
+
return new Response(JSON.stringify(outputs), {
|
|
302
|
+
headers: {
|
|
303
|
+
'Content-Type': 'application/json'
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const parseStream = (stream) => {
|
|
309
|
+
if (!stream.trim())
|
|
310
|
+
return true;
|
|
311
|
+
const start = [];
|
|
312
|
+
const finish = [];
|
|
313
|
+
const delta = [];
|
|
314
|
+
const pattern = /^([^[\]]+)(\[([SFD]+)\])?/;
|
|
315
|
+
for (const raw of stream.split(',')) {
|
|
316
|
+
const match = pattern.exec(raw);
|
|
317
|
+
if (!match)
|
|
318
|
+
continue;
|
|
319
|
+
const name = match[1]?.trim();
|
|
320
|
+
const flags = match[2];
|
|
321
|
+
if (!name)
|
|
322
|
+
continue;
|
|
323
|
+
if (!flags || flags.includes('S'))
|
|
324
|
+
start.push(name);
|
|
325
|
+
if (!flags || flags.includes('F'))
|
|
326
|
+
finish.push(name);
|
|
327
|
+
if (!flags || flags.includes('D'))
|
|
328
|
+
delta.push(name);
|
|
329
|
+
}
|
|
330
|
+
return { start, finish, delta };
|
|
331
|
+
};
|
|
332
|
+
async function streamGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logActivity, logTrace, stream, streamNode, }) {
|
|
333
|
+
const { run, processor, getSSEStream } = createProcessor(project, {
|
|
334
|
+
inputs,
|
|
335
|
+
graph: graphId,
|
|
336
|
+
pluginSettings,
|
|
337
|
+
openAiKey: openaiApiKey,
|
|
338
|
+
openAiEndpoint: openaiEndpoint,
|
|
339
|
+
openAiOrganization: openaiOrganization,
|
|
340
|
+
includeTrace: logTrace
|
|
341
|
+
});
|
|
342
|
+
if (streamNode) {
|
|
343
|
+
let sseStream;
|
|
344
|
+
if (exposeCost || exposeUsage) {
|
|
345
|
+
const baseOptions = {
|
|
346
|
+
exposeCost,
|
|
347
|
+
exposeUsage,
|
|
348
|
+
done: exposeCost || exposeUsage,
|
|
349
|
+
removeFinalOutput: exposeCost || exposeUsage,
|
|
350
|
+
error: logActivity
|
|
351
|
+
};
|
|
352
|
+
const spec = {
|
|
353
|
+
...baseOptions,
|
|
354
|
+
partialOutputs: [streamNode],
|
|
355
|
+
nodeFinish: [streamNode],
|
|
356
|
+
};
|
|
357
|
+
sseStream = getSingleNodeStream(processor, spec);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
sseStream = getSingleNodeStream(processor, streamNode);
|
|
361
|
+
}
|
|
362
|
+
run().catch((err) => {
|
|
363
|
+
console.error(err);
|
|
364
|
+
});
|
|
365
|
+
return sseStream;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
const parsed = parseStream(stream);
|
|
369
|
+
const baseOptions = {
|
|
370
|
+
exposeCost,
|
|
371
|
+
exposeUsage,
|
|
372
|
+
done: exposeCost || exposeUsage,
|
|
373
|
+
removeFinalOutput: exposeCost || exposeUsage,
|
|
374
|
+
error: logActivity
|
|
375
|
+
};
|
|
376
|
+
const sseStream = getSSEStream(parsed === true
|
|
377
|
+
? { ...baseOptions, nodeStart: true, nodeFinish: true, partialOutputs: true }
|
|
378
|
+
: {
|
|
379
|
+
...baseOptions,
|
|
380
|
+
nodeStart: parsed.start,
|
|
381
|
+
nodeFinish: parsed.finish,
|
|
382
|
+
partialOutputs: parsed.delta
|
|
383
|
+
});
|
|
384
|
+
run().catch((err) => {
|
|
385
|
+
console.error(err);
|
|
386
|
+
});
|
|
387
|
+
return sseStream;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function runGraph({ project, inputs, graphId, pluginSettings, openaiApiKey, openaiEndpoint, openaiOrganization, exposeCost, exposeUsage, logTrace }) {
|
|
391
|
+
const { run } = createProcessor(project, {
|
|
392
|
+
inputs,
|
|
393
|
+
graph: graphId,
|
|
394
|
+
pluginSettings,
|
|
395
|
+
openAiKey: openaiApiKey,
|
|
396
|
+
openAiEndpoint: openaiEndpoint,
|
|
397
|
+
openAiOrganization: openaiOrganization,
|
|
398
|
+
includeTrace: logTrace
|
|
399
|
+
});
|
|
400
|
+
const outputs = await run();
|
|
401
|
+
if (!exposeCost) {
|
|
402
|
+
delete outputs.cost;
|
|
403
|
+
}
|
|
404
|
+
if (!exposeUsage) {
|
|
405
|
+
delete outputs.usage;
|
|
406
|
+
delete outputs.requestTokens;
|
|
407
|
+
delete outputs.responseTokens;
|
|
408
|
+
}
|
|
409
|
+
return outputs;
|
|
410
|
+
}
|
|
411
|
+
function throwIfNoMainGraph(project, graph, projectFilePath) {
|
|
412
|
+
if (!project.metadata.mainGraphId && !graph) {
|
|
413
|
+
const validGraphs = Object.values(project.graphs).map((graph) => [graph.metadata.id, graph.metadata.name]);
|
|
414
|
+
if (validGraphs.length === 0) {
|
|
415
|
+
throw new Error('No graphs found in the project file. Please edit the project file in Rivet and add a graph.');
|
|
416
|
+
}
|
|
417
|
+
const validGraphNames = validGraphs.map(([id, name]) => `• "${name}" (${id})`);
|
|
418
|
+
const firstExample = `rivet serve ${projectFilePath} --graph ${validGraphs[0][0]}`;
|
|
419
|
+
const secondExample = `rivet serve ${projectFilePath} --graph "${validGraphs[0][1]}"`;
|
|
420
|
+
throw new Error(`No graph name provided, and project does not specify a main graph. Valid graphs are: \n\n${validGraphNames.join('\n')}\n\n Use either the graph's name or its ID. For example, \n• \`${chalk.bold(firstExample)}\` or\n• \`${chalk.bold(secondExample)}\``);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function throwIfInvalidGraph(project, graph) {
|
|
424
|
+
if (project.metadata.mainGraphId && !graph) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const matchingGraph = Object.values(project.graphs).find((g) => g.metadata.id === graph || g.metadata.name === graph);
|
|
428
|
+
if (!matchingGraph) {
|
|
429
|
+
const validGraphsAndIds = Object.values(project.graphs).flatMap((graph) => [
|
|
430
|
+
graph.metadata.id,
|
|
431
|
+
graph.metadata.name,
|
|
432
|
+
]);
|
|
433
|
+
const suggestion = didYouMean(graph, validGraphsAndIds);
|
|
434
|
+
if (suggestion) {
|
|
435
|
+
throw new Error(`Graph "${graph}" not found in project file. Did you mean \`${chalk.bold(`--graph "${suggestion}"`)}\`?`);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
const validGraphsAndIds = Object.values(project.graphs)
|
|
439
|
+
.map((graph) => `• "${graph.metadata.name}" (${graph.metadata.id})`)
|
|
440
|
+
.join('\n');
|
|
441
|
+
throw new Error(`Graph "${graph}" not found in project file. Valid graphs are: \n${validGraphsAndIds}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function getProjectFile(initialProjectFilePath) {
|
|
446
|
+
let projectFilePath = initialProjectFilePath ?? (await getProjectFilePathFromDirectory(process.cwd()));
|
|
447
|
+
await throwIfMissingFile(projectFilePath);
|
|
448
|
+
if ((await stat(projectFilePath)).isDirectory()) {
|
|
449
|
+
projectFilePath = await getProjectFilePathFromDirectory(projectFilePath);
|
|
450
|
+
}
|
|
451
|
+
return projectFilePath;
|
|
452
|
+
}
|
|
453
|
+
async function getProjectFilePathFromDirectory(directory) {
|
|
454
|
+
const files = await readdir(directory);
|
|
455
|
+
const projectFiles = files.filter((file) => extname(file) === '.rivet-project');
|
|
456
|
+
if (projectFiles.length === 0) {
|
|
457
|
+
throw new Error('No project file found in the current directory. Project files should end with .rivet-project.');
|
|
458
|
+
}
|
|
459
|
+
if (projectFiles.length > 1) {
|
|
460
|
+
throw new Error(`Multiple project files found in the current directory. Please specify which one to serve: \n${projectFiles.join('\n')}`);
|
|
461
|
+
}
|
|
462
|
+
return join(directory, projectFiles[0]);
|
|
463
|
+
}
|
|
464
|
+
function throwIfNoRootDirectory(directory) {
|
|
465
|
+
throw new Error(`No root directory found at: ${directory}`);
|
|
466
|
+
}
|
|
467
|
+
async function throwIfMissingFile(filePath) {
|
|
468
|
+
const errorMsg = await testIfMissingFile(filePath);
|
|
469
|
+
if (errorMsg) {
|
|
470
|
+
throw new Error(errorMsg);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async function testIfMissingFile(filePath) {
|
|
474
|
+
try {
|
|
475
|
+
await stat(filePath);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
if (err.code !== 'ENOENT') {
|
|
480
|
+
return err instanceof Error ? err.message : String(err);
|
|
481
|
+
}
|
|
482
|
+
if (isAbsolute(filePath)) {
|
|
483
|
+
const parentDir = filePath.split('/').slice(0, -1).join('/');
|
|
484
|
+
const possibleFiles = await readdir(parentDir);
|
|
485
|
+
const suggestion = didYouMean(filePath, possibleFiles);
|
|
486
|
+
if (suggestion) {
|
|
487
|
+
return `Could not find project file "${filePath}". Did you mean "${suggestion}"?`;
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
return `Could not find project file "${filePath}".`;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
const possibleFiles = await readdir(process.cwd());
|
|
495
|
+
const suggestion = didYouMean(filePath, possibleFiles);
|
|
496
|
+
if (suggestion) {
|
|
497
|
+
return `Could not find project file "${filePath}". Did you mean "${suggestion}"?`;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
return `Could not find project file "${filePath}".`;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async function validateProjectRootDirectory(directory) {
|
|
506
|
+
if (!directory) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
await throwIfMissingFile(directory);
|
|
510
|
+
return (await stat(directory)).isDirectory();
|
|
511
|
+
}
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type RivetType = {
|
|
2
|
+
globalRivetNodeRegistry: {
|
|
3
|
+
registerPlugin: (plugin: unknown) => void;
|
|
4
|
+
};
|
|
5
|
+
};
|
|
6
|
+
export declare function setupPlugins(Rivet: RivetType): Promise<Record<string, Record<string, string>>>;
|
|
7
|
+
export declare function logAvailablePluginsInfo(): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as yargs from 'yargs';
|
|
2
|
+
export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
|
|
3
|
+
projectFile: string;
|
|
4
|
+
} & {
|
|
5
|
+
graphName: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
"inputs-stdin": boolean;
|
|
8
|
+
} & {
|
|
9
|
+
"include-cost": boolean;
|
|
10
|
+
} & {
|
|
11
|
+
context: string[] | never[];
|
|
12
|
+
} & {
|
|
13
|
+
input: string[] | never[];
|
|
14
|
+
}>;
|
|
15
|
+
export declare function run(args: {
|
|
16
|
+
projectFile: string;
|
|
17
|
+
graphName: string | undefined;
|
|
18
|
+
inputsStdin: boolean;
|
|
19
|
+
includeCost: boolean;
|
|
20
|
+
context: string[];
|
|
21
|
+
input: string[];
|
|
22
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type * as yargs from 'yargs';
|
|
2
|
+
export declare function makeCommand<T>(y: yargs.Argv<T>): yargs.Argv<T & {
|
|
3
|
+
hostname: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
port: number;
|
|
6
|
+
} & {
|
|
7
|
+
dev: boolean;
|
|
8
|
+
} & {
|
|
9
|
+
graph: string | undefined;
|
|
10
|
+
} & {
|
|
11
|
+
"allow-specifying-graph-id": boolean;
|
|
12
|
+
} & {
|
|
13
|
+
"openai-api-key": string | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
"openai-endpoint": string | undefined;
|
|
16
|
+
} & {
|
|
17
|
+
"openai-organization": string | undefined;
|
|
18
|
+
} & {
|
|
19
|
+
"expose-cost": boolean;
|
|
20
|
+
} & {
|
|
21
|
+
"expose-usage": boolean;
|
|
22
|
+
} & {
|
|
23
|
+
stream: string | undefined;
|
|
24
|
+
} & {
|
|
25
|
+
"stream-node": string | undefined;
|
|
26
|
+
} & {
|
|
27
|
+
"log-requests": boolean;
|
|
28
|
+
} & {
|
|
29
|
+
"log-activity": boolean;
|
|
30
|
+
} & {
|
|
31
|
+
"log-trace": boolean;
|
|
32
|
+
} & {
|
|
33
|
+
"projects-root-dir": string | undefined;
|
|
34
|
+
} & {
|
|
35
|
+
projectFile: string | undefined;
|
|
36
|
+
}>;
|
|
37
|
+
type ServerContext = {
|
|
38
|
+
hostname: string | undefined;
|
|
39
|
+
port: number;
|
|
40
|
+
projectFile: string | undefined;
|
|
41
|
+
dev: boolean;
|
|
42
|
+
graph: string | undefined;
|
|
43
|
+
allowSpecifyingGraphId: boolean;
|
|
44
|
+
openaiApiKey: string | undefined;
|
|
45
|
+
openaiEndpoint: string | undefined;
|
|
46
|
+
openaiOrganization: string | undefined;
|
|
47
|
+
exposeCost: boolean;
|
|
48
|
+
exposeUsage: boolean;
|
|
49
|
+
logRequests: boolean;
|
|
50
|
+
logActivity: boolean;
|
|
51
|
+
logTrace: boolean;
|
|
52
|
+
stream: string | undefined;
|
|
53
|
+
streamNode: string | undefined;
|
|
54
|
+
projectsRootDir: string | undefined;
|
|
55
|
+
pluginSettings?: Record<string, Record<string, unknown>>;
|
|
56
|
+
};
|
|
57
|
+
export declare function serve(cliArgs?: Partial<ServerContext>): Promise<void>;
|
|
58
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alpic80/rivet-cli",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"repository": "https://github.com/castortech/rivet",
|
|
5
|
+
"version": "1.24.0-aidon.10",
|
|
6
|
+
"src": "bin/cli.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"rivet": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"types": "dist/types/cli.d.ts",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"files": [
|
|
13
|
+
"bin",
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -b",
|
|
18
|
+
"prepack": "yarn build && cp -r ../../LICENSE ../../README.md .",
|
|
19
|
+
"publish": "yarn npm publish --access public",
|
|
20
|
+
"docker-publish": "./docker-publish.sh",
|
|
21
|
+
"lint": "eslint ./src",
|
|
22
|
+
"test": "tsx --test test/**/*.test.ts",
|
|
23
|
+
"start": "echo START && yarn build && node bin/cli.js",
|
|
24
|
+
"dev:serve": "tsx src/cli.ts serve --projects-root-dir C:/temp/aidon/Rivet_Files"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@alpic80/rivet-core": "^1.24.0-aidon.4",
|
|
28
|
+
"@alpic80/rivet-node": "^1.24.0-aidon.3",
|
|
29
|
+
"@hono/node-server": "^1.13.8",
|
|
30
|
+
"@ironclad/rivet-core": "npm:@alpic80/rivet-core@1.24.0-aidon.4",
|
|
31
|
+
"@ironclad/rivet-node": "npm:@alpic80/rivet-node@1.24.0-aidon.3",
|
|
32
|
+
"@types/dotenv": "^8.2.3",
|
|
33
|
+
"chalk": "^5.4.1",
|
|
34
|
+
"didyoumean2": "^7.0.4",
|
|
35
|
+
"dotenv": "^16.4.7",
|
|
36
|
+
"hono": "^4.7.0",
|
|
37
|
+
"yargs": "^17.7.2"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/yargs": "^17.0.29",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
|
42
|
+
"eslint": "^9.20.1",
|
|
43
|
+
"eslint-config-standard-with-typescript": "^39.1.1",
|
|
44
|
+
"eslint-plugin-import": "^2.31.0",
|
|
45
|
+
"eslint-plugin-n": "^16.2.0",
|
|
46
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
47
|
+
"tsx": "^4.6.2",
|
|
48
|
+
"typescript": "^5.7.3"
|
|
49
|
+
},
|
|
50
|
+
"volta": {
|
|
51
|
+
"node": "20.4.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|