Better edges, ReactFlow v11, AlephAlpha integration, better .gitignores (#139)

* add aleph alpha to installg guide description

* add aleph alpha to settings to add key

* add settings for alephalpha

* add aleph alpha to models

* add aleph alpha api key to keymap

* no visible changes, removed console.log

* current working copy

* added settings for aleph alpha, added test, working api request

* removed console log

* added static build with Aleph Alpha integration

* added static build with Aleph Alpha integration

* Corrected from ALEPH_ALPHA_KEY to ALEPH_ALPHA_API_KEY

* add additional settings requested

* merge conflicts

* add build to gitignore, best practice, reduces conflicts

* remove empty lines

* Delete chainforge/react-server/build directory

build directory should not be in remote

* remove unnecessary changes

* Update backend.ts

* ...

* fixed import

* add aleph alpha to store, remove available llms from modelsettings, remove double palm key input from globalsettings

* Update to ReactFlow v11

* Add remove button to edge on hover

* Quality of life improvements (#133)

* Quality of life improvements for node and python

* Pinned minor version for mantine modules

* Updated package-lock.json

* Added .gitignore to react-server

* Updated .gitignore to python module

---------

Co-authored-by: ianarawjo <fatso784@gmail.com>

* update gitignore

* Minor fixes to AlephAlpha integration code

* Tested npm i and rebuilt react

* Force commit the react build

* Update package version

---------

Co-authored-by: Denise Wagenführ <denise.wagenfuehr@capgemini.com>
Co-authored-by: fguderia <falko.guderian@capgemini.com>
Co-authored-by: denise710 <53524926+denise710@users.noreply.github.com>
Co-authored-by: wday-cs <119377799+wday-cs@users.noreply.github.com>
This commit is contained in:
ianarawjo 2023-09-25 09:44:18 -04:00 committed by GitHub
parent 41bfef557a
commit beeffd0ebb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 5417 additions and 5954 deletions

183
.gitignore vendored
View File

@ -1,13 +1,180 @@
*.DS_Store
chainforge/react-server/node_modules
__pycache__
chainforge/cache
chainforge/examples/oaievals/
# package build folders (sdist)
chainforge.egg-info/
dist/
# == Below was generated by https://www.toptal.com/developers/gitignore/api/python ==
# Edit at https://www.toptal.com/developers/gitignore?templates=python
# venv
venv
node_modules
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
# End of https://www.toptal.com/developers/gitignore/api/python

View File

@ -36,6 +36,7 @@ Though you can run Chainforge, you can't do anything with it without the ability
- HuggingFace models (via the HuggingFace Inference and Inference Endpoints API)
- Anthropic models
- Google PaLM2 chat and text bison models
- Aleph Alpha Luminous Models
- Azure OpenAI Endpoints
- (Locally run) Alpaca and Llama models [Dalai](https://github.com/cocktailpeanut/dalai)-served Alpaca.7b at port 4000.
- To query models like Alpaca and Llama run on your local machine via Dalai, [install `dalai`](https://github.com/cocktailpeanut/dalai) and follow the instructions to download `alpaca.7b`. When everything is setup, run `npx dalai serve 4000`.
@ -48,6 +49,7 @@ To use a specific model provider, you need to do two things:
- HuggingFace: `HUGGINGFACE_API_KEY`
- Anthropic: `ANTHROPIC_API_KEY`
- Google PaLM2: `PALM_API_KEY`
- Aleph Alpha: `ALEPH_ALPHA_API_KEY`
- Azure OpenAI: Set two keys, `AZURE_OPENAI_KEY` and `AZURE_OPENAI_ENDPOINT`. Note that the endpoint should look like a base URL. For examples on what these keys look like, see the [Azure OpenAI documentation](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-javascript).
- When you are done setting the API key(s), **reopen your terminal**. _(This is because the terminal loads the environment variables when it is first opened, so it needs to be refreshed before running `chainforge serve`.)_

View File

@ -0,0 +1 @@
3.10.6/envs/chainforge

View File

@ -446,7 +446,8 @@ def fetchEnvironAPIKeys():
'PALM_API_KEY': 'Google',
'HUGGINGFACE_API_KEY': 'HuggingFace',
'AZURE_OPENAI_KEY': 'Azure_OpenAI',
'AZURE_OPENAI_ENDPOINT': 'Azure_OpenAI_Endpoint'
'AZURE_OPENAI_ENDPOINT': 'Azure_OpenAI_Endpoint',
'ALEPH_ALPHA_API_KEY': 'AlephAlpha'
}
d = { alias: os.environ.get(key) for key, alias in keymap.items() }
ret = jsonify(d)

157
chainforge/react-server/.gitignore vendored Normal file
View File

@ -0,0 +1,157 @@
# Created by https://www.toptal.com/developers/gitignore/api/node,react
# Edit at https://www.toptal.com/developers/gitignore?templates=node,react
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### react ###
.DS_*
**/*.backup.*
**/*.back.*
node_modules
*.sublime*
psd
thumb
sketch
# End of https://www.toptal.com/developers/gitignore/api/node,react

View File

@ -0,0 +1 @@
20.5.1

View File

@ -1,15 +1,15 @@
{
"files": {
"main.css": "/static/css/main.37690c8a.css",
"main.js": "/static/js/main.4fb95aa3.js",
"main.css": "/static/css/main.7d8c3568.css",
"main.js": "/static/js/main.8233b961.js",
"static/js/787.4c72bb55.chunk.js": "/static/js/787.4c72bb55.chunk.js",
"index.html": "/index.html",
"main.37690c8a.css.map": "/static/css/main.37690c8a.css.map",
"main.4fb95aa3.js.map": "/static/js/main.4fb95aa3.js.map",
"main.7d8c3568.css.map": "/static/css/main.7d8c3568.css.map",
"main.8233b961.js.map": "/static/js/main.8233b961.js.map",
"787.4c72bb55.chunk.js.map": "/static/js/787.4c72bb55.chunk.js.map"
},
"entrypoints": [
"static/css/main.37690c8a.css",
"static/js/main.4fb95aa3.js"
"static/css/main.7d8c3568.css",
"static/js/main.8233b961.js"
]
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script async src="https://www.googletagmanager.com/gtag/js?id=G-RN3FDBLMCR"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-RN3FDBLMCR")</script><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A visual programming environment for prompt engineering"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>ChainForge</title><script defer="defer" src="/static/js/main.4fb95aa3.js"></script><link href="/static/css/main.37690c8a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><script async src="https://www.googletagmanager.com/gtag/js?id=G-RN3FDBLMCR"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-RN3FDBLMCR")</script><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A visual programming environment for prompt engineering"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>ChainForge</title><script defer="defer" src="/static/js/main.8233b961.js"></script><link href="/static/css/main.7d8c3568.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -115,6 +115,15 @@ License: MIT
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**
* @license React
* react-dom.production.min.js

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,9 @@
"name": "chain-forge",
"version": "0.1.0",
"private": true,
"engines": {
"node": "^20"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.4.4",
"@azure/openai": "^1.0.0-beta.2",
@ -15,6 +18,7 @@
"@mantine/dropzone": "^6.0.19",
"@mantine/form": "^6.0.11",
"@mantine/prism": "^6.0.15",
"reactflow": "^11.0",
"@reactflow/background": "^11.2.0",
"@reactflow/controls": "^11.1.11",
"@reactflow/core": "^11.7.0",
@ -74,7 +78,6 @@
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
"react-edit-text": "^5.1.0",
"react-flow-renderer": "^10.3.17",
"react-plotly.js": "^2.6.0",
"react-scripts": "5.0.1",
"request": "^2.88.2",

View File

@ -5,10 +5,11 @@ import React, { useState, useCallback, useRef, useEffect } from 'react';
import ReactFlow, {
Controls,
Background,
} from 'react-flow-renderer';
import { Button, Menu, LoadingOverlay, Text, Box, List, Loader, Header, Chip, Badge, Card, Accordion, Tooltip } from '@mantine/core';
} from 'reactflow';
import { Button, Menu, LoadingOverlay, Text, Box, List, Loader, Tooltip } from '@mantine/core';
import { useClipboard } from '@mantine/hooks';
import { IconSettings, IconTextPlus, IconTerminal, IconCsv, IconSettingsAutomation, IconFileSymlink, IconRobot, IconRuler2 } from '@tabler/icons-react';
import RemoveEdge from './RemoveEdge';
import TextFieldsNode from './TextFieldsNode'; // Import a custom node
import PromptNode from './PromptNode';
import EvaluatorNode from './EvaluatorNode';
@ -23,11 +24,14 @@ import GlobalSettingsModal from './GlobalSettingsModal';
import ExampleFlowsModal from './ExampleFlowsModal';
import AreYouSureModal from './AreYouSureModal';
import LLMEvaluatorNode from './LLMEvalNode';
import { getDefaultModelFormData, getDefaultModelSettings, setCustomProviders } from './ModelSettingSchemas';
import { getDefaultModelFormData, getDefaultModelSettings } from './ModelSettingSchemas';
import { v4 as uuid } from 'uuid';
import LZString from 'lz-string';
import { EXAMPLEFLOW_1 } from './example_flows';
import './text-fields-node.css';
// Styling
import 'reactflow/dist/style.css'; // reactflow
import './text-fields-node.css'; // project
// State management (from https://reactflow.dev/docs/guides/state-management/)
import { shallow } from 'zustand/shallow';
@ -70,8 +74,6 @@ const INITIAL_LLM = () => {
return falcon7b;
};
// import AnimatedConnectionLine from './AnimatedConnectionLine';
const nodeTypes = {
textfields: TextFieldsNode, // Register the custom node
prompt: PromptNode,
@ -87,6 +89,10 @@ const nodeTypes = {
comment: CommentNode,
};
const edgeTypes = {
default: RemoveEdge,
};
// Whether we are running on localhost or not, and hence whether
// we have access to the Flask backend for, e.g., Python code evaluation.
const IS_RUNNING_LOCALLY = APP_IS_RUNNING_LOCALLY();
@ -287,7 +293,7 @@ const App = () => {
const uid = (id) => `${id}-${Date.now()}`;
const starting_nodes = [
{ id: uid('prompt'), type: 'prompt', data: {
prompt: 'Why is the sky blue?',
prompt: '',
n: 1,
llms: [INITIAL_LLM()] },
position: { x: 450, y: 200 } },
@ -694,6 +700,7 @@ const App = () => {
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
zoomOnPinch={false}
zoomOnScroll={false}
panOnScroll={true}

View File

@ -3,7 +3,7 @@ import { Text } from '@mantine/core';
import useStore from './store';
import NodeLabel from './NodeLabelComponent'
import { IconCsv } from '@tabler/icons-react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
const CsvNode = ({ data, id }) => {
const setDataPropsForNode = useStore((state) => state.setDataPropsForNode);

View File

@ -1,5 +1,5 @@
import React, { useState, useRef, useCallback, useEffect } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import { Button, Code, Modal, Tooltip, Box, Text } from '@mantine/core';
import { Prism } from '@mantine/prism';
import { useDisclosure } from '@mantine/hooks';

View File

@ -164,6 +164,7 @@ const GlobalSettingsModal = forwardRef((props, ref) => {
Azure_OpenAI: '',
Azure_OpenAI_Endpoint: '',
HuggingFace: '',
AlephAlpha: '',
},
validate: {
@ -209,8 +210,7 @@ return (
placeholder="Paste your OpenAI API key here"
{...form.getInputProps('OpenAI')}
/>
<br />
<br />
<TextInput
label="HuggingFace API Key"
placeholder="Paste your HuggingFace API key here"
@ -231,7 +231,12 @@ return (
{...form.getInputProps('Google')}
/>
<br />
<TextInput
label="Aleph Alpha API Key"
placeholder="Paste your Aleph Alpha API key here"
{...form.getInputProps('AlephAlpha')}
/>
<br />
<Divider my="xs" label="Microsoft Azure" labelPosition="center" />
<TextInput
label="Azure OpenAI Key"

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import useStore from './store';
import NodeLabel from './NodeLabelComponent';
import LLMResponseInspector, { exportToExcel } from './LLMResponseInspector';

View File

@ -1,5 +1,5 @@
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import { Alert, Progress, Textarea } from '@mantine/core';
import { IconAlertTriangle, IconRobot, IconSearch } from "@tabler/icons-react";
import { v4 as uuid } from 'uuid';

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import { Switch, Progress, Textarea, Text, Popover, Center, Modal, Box, Tooltip } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconList } from '@tabler/icons-react';

View File

@ -0,0 +1,68 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@reactflow/core';
import useStore from './store';
const EdgePathContainer = styled.g`
path:nth-child(2) {
pointer-events: all;
&:hover {
& + .edgebutton {
// Make add node button visible
visibility: visible;
}
}
}`;
export default function CustomEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style = {},
markerEnd,
}) {
const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
const [hovering, setHovering] = useState(false);
const removeEdge = useStore((state) => state.removeEdge);
const onEdgeClick = (evt, id) => {
evt.stopPropagation();
removeEdge(id);
};
// Thanks in part to oshanley https://github.com/wbkd/react-flow/issues/1211#issuecomment-1585032930
return (
<EdgePathContainer onPointerEnter={()=>setHovering(true)} onPointerLeave={()=>setHovering(false)} onClick={()=>console.log('click')}>
<BaseEdge path={edgePath} markerEnd={markerEnd} style={{...style, stroke: (hovering ? '#000' : '#999')}} />
<EdgeLabelRenderer>
<div
style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
fontSize: 12,
pointerEvents: 'all',
visibility: hovering ? 'inherit' : 'hidden',
}}
className="nodrag nopan"
>
<button className="remove-edge-btn" onClick={(event) => onEdgeClick(event, id)}>
×
</button>
</div>
</EdgeLabelRenderer>
</EdgePathContainer>
);
}

View File

@ -1,5 +1,5 @@
import { useState, useCallback, useEffect, useRef } from "react";
import { Handle } from "react-flow-renderer";
import { Handle } from "reactflow";
import { NativeSelect, TextInput, Flex, Text, Box, Select, ActionIcon, Menu, Tooltip } from "@mantine/core";
import { IconCaretDown, IconHash, IconRuler2, IconSearch, IconX } from "@tabler/icons-react";
import NodeLabel from "./NodeLabelComponent";

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Handle, useUpdateNodeInternals } from 'react-flow-renderer';
import { Handle, useUpdateNodeInternals } from 'reactflow';
import { Badge } from '@mantine/core';
import useStore from './store'

View File

@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import { Textarea, Tooltip } from '@mantine/core';
import { IconTextPlus, IconEye, IconEyeOff } from '@tabler/icons-react';
import useStore from './store';

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Handle } from 'react-flow-renderer';
import { Handle } from 'reactflow';
import { NativeSelect } from '@mantine/core';
import useStore, { colorPalettes } from './store';
import Plot from 'react-plotly.js';

View File

@ -1,7 +1,7 @@
/*
* @jest-environment jsdom
*/
import { call_anthropic, call_chatgpt, call_google_palm, extract_responses, merge_response_objs } from '../utils';
import { call_alephalpha, call_anthropic, call_chatgpt, call_google_palm, extract_responses, merge_response_objs } from '../utils';
import { LLM, NativeLLM } from '../models';
import { expect, test } from '@jest/globals';
import { LLMResponseObject } from '../typing';
@ -106,3 +106,24 @@ test('google palm2 models', async () => {
expect(typeof resps[0]).toBe('string');
console.log(JSON.stringify(resps));
}, 40000);
test('aleph alpha model', async () => {
let [query, response] = await call_alephalpha("Who invented modern playing cards?", NativeLLM.Aleph_Alpha_Luminous_Base, 3, 0.7);
expect(response).toHaveLength(3);
// Extract responses, check their type
let resps = extract_responses(response, NativeLLM.Aleph_Alpha_Luminous_Base);
expect(resps).toHaveLength(3);
expect(typeof resps[0]).toBe('string');
console.log(JSON.stringify(resps));
// Call Google's PaLM Text Completions API with a basic question
[query, response] = await call_alephalpha("Who invented modern playing cards? The answer ", NativeLLM.Aleph_Alpha_Luminous_Base, 3, 0.7);
expect(response).toHaveLength(3);
// Extract responses, check their type
resps = extract_responses(response, NativeLLM.Aleph_Alpha_Luminous_Base);
expect(resps).toHaveLength(3);
expect(typeof resps[0]).toBe('string');
console.log(JSON.stringify(resps));
}, 40000);

View File

@ -548,6 +548,7 @@ export async function queryLLM(id: string,
// Ensure llm param is an array
if (typeof llm === 'string')
llm = [ llm ];
llm = llm as (Array<string> | Array<Dict>);
await setAPIKeys(api_keys);
@ -1162,4 +1163,4 @@ export async function loadCachedCustomProviders(): Promise<Dict> {
}).then(function(res) {
return res.json();
});
}
}

View File

@ -46,6 +46,14 @@ export enum NativeLLM {
PaLM2_Text_Bison = "text-bison-001", // it's really models/text-bison-001, but that's confusing
PaLM2_Chat_Bison = "chat-bison-001",
// Aleph Alpha
Aleph_Alpha_Luminous_Extended = "luminous-extended",
Aleph_Alpha_Luminous_ExtendedControl = "luminous-extended-control",
Aleph_Alpha_Luminous_BaseControl = "luminous-base-control",
Aleph_Alpha_Luminous_Base = "luminous-base",
Aleph_Alpha_Luminous_Supreme = "luminous-supreme",
Aleph_Alpha_Luminous_SupremeControl = "luminous-supreme-control",
// HuggingFace Inference hosted models, suggested to users
HF_GPT2 = "gpt2",
HF_BLOOM_560M = "bigscience/bloom-560m",
@ -71,6 +79,7 @@ export enum LLMProvider {
Anthropic = "anthropic",
Google = "google",
HuggingFace = "hf",
Aleph_Alpha = "alephalpha",
Custom = "__custom",
}
@ -93,8 +102,11 @@ export function getProvider(llm: LLM): LLMProvider | undefined {
return LLMProvider.HuggingFace;
else if (llm.toString().startsWith('claude'))
return LLMProvider.Anthropic;
else if (llm_name?.startsWith('Aleph_Alpha'))
return LLMProvider.Aleph_Alpha;
else if (llm.toString().startsWith('__custom/'))
return LLMProvider.Custom;
return undefined;
}

View File

@ -97,6 +97,7 @@ let GOOGLE_PALM_API_KEY = get_environ("PALM_API_KEY");
let AZURE_OPENAI_KEY = get_environ("AZURE_OPENAI_KEY");
let AZURE_OPENAI_ENDPOINT = get_environ("AZURE_OPENAI_ENDPOINT");
let HUGGINGFACE_API_KEY = get_environ("HUGGINGFACE_API_KEY");
let ALEPH_ALPHA_API_KEY = get_environ("ALEPH_ALPHA_API_KEY");
/**
* Sets the local API keys for the revelant LLM API(s).
@ -117,6 +118,8 @@ export function set_api_keys(api_keys: StringDict): void {
AZURE_OPENAI_KEY = api_keys['Azure_OpenAI'];
if (key_is_present('Azure_OpenAI_Endpoint'))
AZURE_OPENAI_ENDPOINT = api_keys['Azure_OpenAI_Endpoint'];
if (key_is_present('AlephAlpha'))
ALEPH_ALPHA_API_KEY = api_keys['AlephAlpha'];
// Soft fail for non-present keys
}
@ -621,6 +624,43 @@ export async function call_huggingface(prompt: string, model: LLM, n: number = 1
return [query, responses];
}
export async function call_alephalpha(prompt: string, model: LLM, n: number = 1, temperature: number = 1.0, params?: Dict): Promise<[Dict, Dict]> {
if (!ALEPH_ALPHA_API_KEY)
throw Error("Could not find an API key for Aleph Alpha models. Double-check that your API key is set in Settings or in your local environment.");
const url: string = 'https://api.aleph-alpha.com/complete';
let headers: StringDict = {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (ALEPH_ALPHA_API_KEY !== undefined)
headers.Authorization = `Bearer ${ALEPH_ALPHA_API_KEY}`;
let data = JSON.stringify({
"model": model.toString(),
"prompt": prompt,
"n": n,
...params
});
// Setup the args for the query
let query: Dict = {
model: model.toString(),
n: n,
temperature: temperature,
...params, // 'the rest' of the settings, passed from the front-end settings
};
const response = await fetch(url, {
headers: headers,
method: "POST",
body: data,
});
const result = await response.json();
const responses = await result.completions?.map((x: any) => x.completion);
return [query, responses];
}
async function call_custom_provider(prompt: string, model: LLM, n: number = 1, temperature: number = 1.0, params?: Dict): Promise<[Dict, Dict]> {
if (!APP_IS_RUNNING_LOCALLY())
throw new Error("The ChainForge app does not appear to be running locally. You can only call custom model providers if you are running ChainForge on your local machine, from a Flask app.")
@ -651,7 +691,6 @@ async function call_custom_provider(prompt: string, model: LLM, n: number = 1, t
responses.push(response);
}
return [query, responses];
}
@ -662,7 +701,7 @@ export async function call_llm(llm: LLM, prompt: string, n: number, temperature:
// Get the correct API call for the given LLM:
let call_api: LLMAPICall | undefined;
let llm_provider: LLMProvider = getProvider(llm);
if (llm_provider === undefined)
throw new Error(`Language model ${llm} is not supported.`);
@ -678,6 +717,8 @@ export async function call_llm(llm: LLM, prompt: string, n: number, temperature:
call_api = call_anthropic;
else if (llm_provider === LLMProvider.HuggingFace)
call_api = call_huggingface;
else if (llm_provider === LLMProvider.Aleph_Alpha)
call_api = call_alephalpha;
else if (llm_provider === LLMProvider.Custom)
call_api = call_custom_provider;
@ -755,13 +796,19 @@ function _extract_huggingface_responses(response: Array<Dict>): Array<string>{
return response.map((r: Dict) => r.generated_text.trim());
}
/**
* Extracts the text part of a Aleph Alpha text completion.
*/
function _extract_alephalpha_responses(response: Dict): Array<string> {
return response.map((r: string) => r.trim());
}
/**
* Given a LLM and a response object from its API, extract the
* text response(s) part of the response object.
*/
export function extract_responses(response: Array<string | Dict> | Dict, llm: LLM | string): Array<string> {
let llm_provider: LLMProvider = getProvider(llm as LLM);
switch (llm_provider) {
case LLMProvider.OpenAI:
if (llm.toString().toLowerCase().includes('davinci'))
@ -778,6 +825,8 @@ export function extract_responses(response: Array<string | Dict> | Dict, llm: LL
return _extract_anthropic_responses(response as Dict[]);
case LLMProvider.HuggingFace:
return _extract_huggingface_responses(response as Dict[]);
case LLMProvider.Aleph_Alpha:
return _extract_alephalpha_responses(response);
default:
if (Array.isArray(response) && response.length > 0 && typeof response[0] === 'string')
return response as string[];

View File

@ -3,7 +3,7 @@ import {
addEdge,
applyNodeChanges,
applyEdgeChanges,
} from 'react-flow-renderer';
} from 'reactflow';
import { escapeBraces } from './backend/template';
import { filterDict } from './backend/utils';
import { APP_IS_RUNNING_LOCALLY } from './backend/utils';
@ -35,6 +35,7 @@ export let initLLMProviders = [
{ name: "PaLM2", emoji: "🦬", model: "chat-bison-001", base_model: "palm2-bison", temp: 0.7 },
{ name: "Azure OpenAI", emoji: "🔷", model: "azure-openai", base_model: "azure-openai", temp: 1.0 },
{ name: "HuggingFace", emoji: "🤗", model: "tiiuae/falcon-7b-instruct", base_model: "hf", temp: 1.0 },
{ name: "Aleph Alpha", emoji: "💡", model: "luminous-base", base_model: "luminous-base", temp: 0.0 }
];
if (APP_IS_RUNNING_LOCALLY()) {
initLLMProviders.push({ name: "Dalai (Alpaca.7B)", emoji: "🦙", model: "alpaca.7B", base_model: "dalai", temp: 0.5 });
@ -238,6 +239,11 @@ const useStore = create((set, get) => ({
edges: newedges
});
},
removeEdge: (id) => {
set({
edges: applyEdgeChanges([{id: id, type: 'remove'}], get().edges),
});
},
onNodesChange: (changes) => {
set({
nodes: applyNodeChanges(changes, get().nodes),
@ -262,8 +268,9 @@ const useStore = create((set, get) => ({
get().setDataPropsForNode(target.id, { refresh: true });
}
connection.interactionWidth = 100;
connection.interactionWidth = 40;
connection.markerEnd = {type: 'arrow', width: '22px', height: '22px'};
connection.type = 'default';
set({
edges: addEdge(connection, get().edges) // get().edges.concat(connection)

View File

@ -6,11 +6,6 @@
min-width: 200px;
}
path.react-flow__edge-path:hover {
stroke: #222;
stroke-width: 2px;
}
hr {
border: none;
background-color: #bbb;
@ -122,7 +117,24 @@
font-size: 10pt;
color: #777;
}
button.remove-edge-btn {
border-radius: 50%;
border-color: #ddd;
border-style: solid;
background-color: #ddd;
cursor:pointer;
}
button.remove-edge-btn:hover {
background-color: white;
border-color: #fff;
}
button.remove-edge-btn:active {
background-color: #777;
color: #fff;
border-color: #777;
}
.input-field {
display: flex;
align-items: center;

View File

@ -6,7 +6,7 @@ def readme():
setup(
name='chainforge',
version='0.2.6.2',
version='0.2.6.3',
packages=find_packages(),
author="Ian Arawjo",
description="A Visual Programming Environment for Prompt Engineering",