chainforge 0.1.0__tar.gz

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.
Files changed (42) hide show
  1. chainforge-0.1.0/LICENSE.md +21 -0
  2. chainforge-0.1.0/MANIFEST.in +1 -0
  3. chainforge-0.1.0/PKG-INFO +15 -0
  4. chainforge-0.1.0/README.md +77 -0
  5. chainforge-0.1.0/chainforge/__init__.py +1 -0
  6. chainforge-0.1.0/chainforge/app.py +123 -0
  7. chainforge-0.1.0/chainforge/flask_app.py +654 -0
  8. chainforge-0.1.0/chainforge/promptengine/__init__.py +1 -0
  9. chainforge-0.1.0/chainforge/promptengine/dalaipy/__init__.py +2 -0
  10. chainforge-0.1.0/chainforge/promptengine/dalaipy/main.py +109 -0
  11. chainforge-0.1.0/chainforge/promptengine/models.py +39 -0
  12. chainforge-0.1.0/chainforge/promptengine/query.py +201 -0
  13. chainforge-0.1.0/chainforge/promptengine/template.py +179 -0
  14. chainforge-0.1.0/chainforge/promptengine/utils.py +249 -0
  15. chainforge-0.1.0/chainforge/react-server/build/.DS_Store +0 -0
  16. chainforge-0.1.0/chainforge/react-server/build/asset-manifest.json +15 -0
  17. chainforge-0.1.0/chainforge/react-server/build/favicon.ico +0 -0
  18. chainforge-0.1.0/chainforge/react-server/build/index.html +1 -0
  19. chainforge-0.1.0/chainforge/react-server/build/logo192.png +0 -0
  20. chainforge-0.1.0/chainforge/react-server/build/logo512.png +0 -0
  21. chainforge-0.1.0/chainforge/react-server/build/manifest.json +25 -0
  22. chainforge-0.1.0/chainforge/react-server/build/robots.txt +3 -0
  23. chainforge-0.1.0/chainforge/react-server/build/static/.DS_Store +0 -0
  24. chainforge-0.1.0/chainforge/react-server/build/static/css/main.f10de766.css +2 -0
  25. chainforge-0.1.0/chainforge/react-server/build/static/css/main.f10de766.css.map +1 -0
  26. chainforge-0.1.0/chainforge/react-server/build/static/favicon.ico +0 -0
  27. chainforge-0.1.0/chainforge/react-server/build/static/js/787.4c72bb55.chunk.js +2 -0
  28. chainforge-0.1.0/chainforge/react-server/build/static/js/787.4c72bb55.chunk.js.map +1 -0
  29. chainforge-0.1.0/chainforge/react-server/build/static/js/main.008770c8.js +3 -0
  30. chainforge-0.1.0/chainforge/react-server/build/static/js/main.008770c8.js.LICENSE.txt +178 -0
  31. chainforge-0.1.0/chainforge/react-server/build/static/js/main.008770c8.js.map +1 -0
  32. chainforge-0.1.0/chainforge/react-server/build/static/logo192.png +0 -0
  33. chainforge-0.1.0/chainforge/react-server/build/static/logo512.png +0 -0
  34. chainforge-0.1.0/chainforge/react-server/build/static/manifest.json +25 -0
  35. chainforge-0.1.0/chainforge.egg-info/PKG-INFO +15 -0
  36. chainforge-0.1.0/chainforge.egg-info/SOURCES.txt +40 -0
  37. chainforge-0.1.0/chainforge.egg-info/dependency_links.txt +1 -0
  38. chainforge-0.1.0/chainforge.egg-info/entry_points.txt +2 -0
  39. chainforge-0.1.0/chainforge.egg-info/requires.txt +8 -0
  40. chainforge-0.1.0/chainforge.egg-info/top_level.txt +1 -0
  41. chainforge-0.1.0/setup.cfg +4 -0
  42. chainforge-0.1.0/setup.py +38 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Ian Arawjo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ graft chainforge/react-server/build
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.1
2
+ Name: chainforge
3
+ Version: 0.1.0
4
+ Summary: A Visual Programming Environment for Prompt Engineering
5
+ Home-page: https://github.com/ianarawjo/ChainForge/
6
+ Author: Ian Arawjo
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.7
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Requires-Python: >=3.7
15
+ License-File: LICENSE.md
@@ -0,0 +1,77 @@
1
+ # ⛓️🛠️ ChainForge
2
+ **An open-source visual programming environment for battle-testing prompts to LLMs.**
3
+
4
+ <img width="1615" alt="Screen Shot 2023-05-17 at 2 45 17 PM" src="https://github.com/ianarawjo/ChainForge/assets/5251713/96aecea7-cf05-4064-8f83-20a524449af7">
5
+
6
+ ChainForge is a data flow prompt engineering environment for analyzing and evaluating LLM responses. Like Jupyter Notebooks are geared towards early-stage exploration, ChainForge is geared towards early-stage, quick-and-dirty exploration of prompts and response quality that goes beyond ad-hoc chatting with individual LLMs. With CF, you can:
7
+ - Query multiple LLMs at once to sketch prompt ideas and test variations quickly and effectively.
8
+ - Compare response quality across prompt variations and across models to choose the best prompt and model for your use case.
9
+ - Setup an evaluation metric (scoring function) and immediately visualize results across prompts, prompt parameters, and models.
10
+
11
+ **This is an open alpha of Chainforge.** We currently support models GPT3.5, GPT4, Claude, and Alpaca 7B (through [Dalai](https://github.com/cocktailpeanut/dalai)) at default settings. Try it and let us know what you think! :)
12
+
13
+ ChainForge is built on [ReactFlow](https://reactflow.dev) and [Flask](https://flask.palletsprojects.com/en/2.3.x/).
14
+
15
+ # Installation
16
+
17
+ To get started with Chainforge alpha, see the [Installation Guide](https://github.com/ianarawjo/ChainForge/blob/main/GUIDE.md). In the near future, we will upload to PyPI as an official package.
18
+
19
+ ## Example evaluation flows
20
+
21
+ We've prepared a couple Example flows to give you a sense of what's possible with Chainforge.
22
+ Import them, then:
23
+ - Run any Prompt node(s) to query the LLM(s),
24
+ - Run any Evaluator nodes to score responses.
25
+
26
+ Note that right now, **exporting a CF flow does not save cache'd responses.**
27
+
28
+ # Features
29
+
30
+ A key goal of ChainForge is facilitating **comparison** and **evaluation** of prompts and models, and (in the near future) prompt chains. Basic features are:
31
+ - **Prompt permutations**: Setup a prompt template and feed it variations of input variables. ChainForge will prompt all selected LLMs with all possible permutations of the input prompt, so that you can get a better sense of prompt quality. You can also chain prompt templates at arbitrary depth (e.g., to compare templates).
32
+ - **Evaluation nodes**: Probe LLM responses in a chain and test them (classically) for some desired behavior. At a basic level, this is Python script based. We plan to add preset evaluator nodes for common use cases in the near future (e.g., name-entity recognition). Note that you can also chain LLM responses into prompt templates to help evaluate outputs cheaply before more extensive evaluation methods.
33
+ - **Visualization nodes**: Visualize evaluation results on plots like box-and-whisker and 3D scatterplots.
34
+
35
+ Taken together, these three features let you easily:
36
+ - **Compare across prompts and prompt parameters**: Choose the best set of prompts that maximizes your eval target metrics (e.g., lowest code error rate). Or, see how changing parameters in a prompt template affects the quality of responses.
37
+ - **Compare across models**: Compare responses for every prompt across models.
38
+
39
+ # Development
40
+
41
+ ChainForge is being developed by research scientists at Harvard University in the [Harvard HCI](https://hci.seas.harvard.edu) group:
42
+ - [Ian Arawjo](http://ianarawjo.com/index.html)
43
+ - [Priyan Vaithilingam](https://priyan.info)
44
+ - [Elena Glassman](https://glassmanlab.seas.harvard.edu/glassman.html)
45
+
46
+ We provide ongoing releases of this tool in the hopes that others find it useful for their projects.
47
+
48
+ ## Future Planned Features
49
+
50
+ - **Model settings**: Change settings for individual models, so one can test across the same model with different settings
51
+ - **Compare across response batches**: Run an evaluator over all N responses generated for each prompt, to measure factors like variability or parseability (e.g., how many code outputs pass a basic smell test?)
52
+ - **System prompts**: Ability to change the system prompt for models that support it (e.g., ChatGPT). Try out different system prompts and compare response quality.
53
+ - **Collapse nodes**: Nodes should be collapseable, to save screen space.
54
+ - **LMQL and Microsoft guidance nodes**: Support for prompt pipelines that involve LMQL and {{guidance}} code, esp. inspecting masked response variables.
55
+ - **AI assistance for prompt engineering**: Spur creative ideas and quickly iterate on variations of prompts through interaction with GPT4.
56
+ - **Compare fine-tuned to base models**: Beyond comparing between different models like Alpaca and ChatGPT, support comparison between versions of the same model (e.g., a base model and a fine-tuned one). Helper users detect where fine-tuning resulted in any 'breaking changes' elsewhere.
57
+ - **Export to code**: In the future, export prompt and (potentially) chains using a programming API like LangChain.
58
+ - **Dark mode**: A dark mode theme
59
+ - **Compare across chains**: If a prompt P is used *across* chains C1 C2 etc, how does changing it affect all downstream events?
60
+
61
+ See a feature you'd like that isn't here? Open an [Issue](https://github.com/ianarawjo/ChainForge/issues).
62
+
63
+ ## Inspiration and Links
64
+
65
+ ChainForge is meant to be general-purpose, and is not developed for a specific API or LLM back-end. Our ultimate goal is integration into other tools for the systematic evaluation and auditing of LLMs. We hope to help others who are developing prompt-analysis flows in LLMs, or otherwise auditing LLM outputs. This project was inspired by own our use case, but also shares some comraderie with two related (closed-source) research projects, both led by [Sherry Wu](https://www.cs.cmu.edu/~sherryw/):
66
+ - "PromptChainer: Chaining Large Language Model Prompts through Visual Programming" (Wu et al., CHI ’22 LBW) [Video](https://www.youtube.com/watch?v=p6MA8q19uo0)
67
+ - "AI Chains: Transparent and Controllable Human-AI Interaction by Chaining Large Language Model Prompts" (Wu et al., CHI ’22)
68
+
69
+ Unlike these projects, we are focusing on supporting evaluation across prompts, prompt parameters, and models.
70
+
71
+ ## How to collaborate?
72
+
73
+ We are looking for open-source collaborators. The best way to do this, at the moment, is simply to implement the requested feature / bug fix and submit a Pull Request. If you want to report a bug or request a feature, open an [Issue](https://github.com/ianarawjo/ChainForge/issues).
74
+
75
+ # License
76
+
77
+ ChainForge is released under the MIT License.
@@ -0,0 +1 @@
1
+ from .app import main
@@ -0,0 +1,123 @@
1
+ import json, os, asyncio, sys, argparse, threading
2
+ from dataclasses import dataclass
3
+ from statistics import mean, median, stdev
4
+ from flask import Flask, request, jsonify
5
+ from flask_cors import CORS
6
+ from flask_socketio import SocketIO
7
+ from chainforge.flask_app import run_server
8
+ from chainforge.promptengine.query import PromptLLM, PromptLLMDummy
9
+ from chainforge.promptengine.template import PromptTemplate, PromptPermutationGenerator
10
+ from chainforge.promptengine.utils import LLM, is_valid_filepath, get_files_at_dir, create_dir_if_not_exists
11
+
12
+ # Setup the socketio app
13
+ app = Flask(__name__)
14
+
15
+ # Initialize Socket.IO with CORS enabled
16
+ socketio = SocketIO(app, cors_allowed_origins="*", async_mode="gevent")
17
+
18
+ # The cache base directory
19
+ CACHE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'cache')
20
+
21
+ # Wait a max of a full 3 minutes (180 seconds) for the response count to update, before exiting.
22
+ MAX_WAIT_TIME = 180
23
+
24
+ def countdown():
25
+ n = 10
26
+ while n > 0:
27
+ socketio.sleep(0.5)
28
+ socketio.emit('response', n, namespace='/queryllm')
29
+ n -= 1
30
+
31
+ @socketio.on('queryllm', namespace='/queryllm')
32
+ def readCounts(data):
33
+ id = data['id']
34
+ max_count = data['max']
35
+ tempfilepath = os.path.join(CACHE_DIR, f'_temp_{id}.txt')
36
+
37
+ # Check that temp file exists. If it doesn't, something went wrong with setup on Flask's end:
38
+ if not os.path.exists(tempfilepath):
39
+ print(f"Error: Temp file not found at path {tempfilepath}. Cannot stream querying progress.")
40
+ socketio.emit('finish', 'temp file not found', namespace='/queryllm')
41
+
42
+ i = 0
43
+ last_n = 0
44
+ init_run = True
45
+ while i < MAX_WAIT_TIME and last_n < max_count:
46
+
47
+ # Open the temp file to read the progress so far:
48
+ try:
49
+ with open(tempfilepath, 'r') as f:
50
+ queries = json.load(f)
51
+ except FileNotFoundError as e:
52
+ # If the temp file was deleted during executing, the Flask 'queryllm' func must've terminated successfully:
53
+ socketio.emit('finish', 'success', namespace='/queryllm')
54
+ return
55
+
56
+ # Calculate the total sum of responses
57
+ # TODO: This is a naive approach; we need to make this more complex and factor in cache'ing in future
58
+ n = sum([int(n) for llm, n in queries.items()])
59
+
60
+ # If something's changed...
61
+ if init_run or last_n != n:
62
+ i = 0
63
+ last_n = n
64
+ init_run = False
65
+
66
+ # Update the React front-end with the current progress
67
+ socketio.emit('response', queries, namespace='/queryllm')
68
+
69
+ else:
70
+ i += 0.1
71
+
72
+ # Wait a bit before reading the file again
73
+ socketio.sleep(0.1)
74
+
75
+ if i >= MAX_WAIT_TIME:
76
+ print(f"Error: Waited maximum {MAX_WAIT_TIME} seconds for response count to update. Exited prematurely.")
77
+ socketio.emit('finish', 'max_wait_reached', namespace='/queryllm')
78
+ else:
79
+ print("All responses loaded!")
80
+ socketio.emit('finish', 'success', namespace='/queryllm')
81
+
82
+ # Start socketio server
83
+ def run_socketio_server(socketio, port):
84
+ socketio.run(app, host="localhost", port=8001)
85
+
86
+ # Main Chainforge start
87
+ def main():
88
+ parser = argparse.ArgumentParser(description='Chainforge command line tool')
89
+
90
+ # Serve command
91
+ subparsers = parser.add_subparsers(dest='serve')
92
+ serve_parser = subparsers.add_parser('serve', help='Start Chainforge server')
93
+
94
+ # Turn on to disable all outbound LLM API calls and replace them with dummy calls
95
+ # that return random strings of ASCII characters. Useful for testing interface without wasting $$.
96
+ serve_parser.add_argument('--dummy-responses',
97
+ help="""Disables queries to LLMs, replacing them with spoofed responses composed of random ASCII characters.
98
+ Produces each dummy response at random intervals between 0.1 and 3 seconds.""",
99
+ dest='dummy_responses',
100
+ action='store_true')
101
+
102
+ # TODO: Reimplement this where the React server is given the backend's port before loading.
103
+ # serve_parser.add_argument('--port', help='The port to run the server on. Defaults to 8000.', type=int, default=8000, nargs='?')
104
+
105
+ args = parser.parse_args()
106
+
107
+ # Currently only support the 'serve' command...
108
+ if not args.serve:
109
+ parser.print_help()
110
+ exit(0)
111
+
112
+ port = 8000 # args.port if args.port else 8000
113
+
114
+ # Spin up separate thread for socketio app, on port+1 (8001 default)
115
+ print(f"Serving SocketIO server on port {port+1}...")
116
+ t1 = threading.Thread(target=run_socketio_server, args=[socketio, port+1])
117
+ t1.start()
118
+
119
+ print(f"Serving Flask server on port {port}...")
120
+ run_server(host="localhost", port=port, cmd_args=args)
121
+
122
+ if __name__ == "__main__":
123
+ main()