├── .editorconfig
├── .env.template
├── .eslintignore
├── .gitignore
├── .prettierignore
├── README.md
├── docs
├── example.gif
└── examples
│ └── webpage-extract.png
├── package-lock.json
├── package.json
├── public
├── favicon.png
├── favicon.svg
├── index.html
├── logo@2x.png
├── robots.txt
└── workflow-examples
│ ├── initial.json
│ ├── sentiment-analysis.json
│ └── webpage-parsing.json
├── src
├── App.tsx
├── components
│ ├── editable.tsx
│ ├── modal
│ │ ├── AddNodeModal.tsx
│ │ ├── ConfigureMappingModal.tsx
│ │ └── index.tsx
│ ├── nodes.tsx
│ └── nodes
│ │ ├── audioTranscription.tsx
│ │ ├── destinations
│ │ ├── connection.tsx
│ │ └── template.tsx
│ │ ├── dropArea.tsx
│ │ ├── extract.tsx
│ │ ├── loop.tsx
│ │ ├── nodeDivider.tsx
│ │ ├── sources
│ │ ├── connection.tsx
│ │ ├── connectionConfig
│ │ │ ├── googleSheetConfig.tsx
│ │ │ └── hubspotConfig.tsx
│ │ ├── file.tsx
│ │ ├── index.tsx
│ │ ├── static.tsx
│ │ └── url.tsx
│ │ └── summarize.tsx
├── index.css
├── index.tsx
├── reportWebVitals.ts
├── types
│ ├── index.tsx
│ ├── node.tsx
│ ├── spyglassApi.tsx
│ └── typeutils.ts
├── utils
│ ├── metrics.tsx
│ ├── nodeUtils.ts
│ └── workflowValidator.ts
└── workflows
│ ├── executor.tsx
│ ├── index.tsx
│ ├── task-executor.tsx
│ ├── utils.tsx
│ └── workflowinstance.ts
├── tailwind.config.js
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # Unix-style newlines with a newline ending every file
4 | [*]
5 | end_of_line = lf
6 | insert_final_newline = true
7 |
8 | # Matches multiple files with brace expansion notation
9 | # Set default charset
10 | [*.{css,tsx,html,json}]
11 | charset = utf-8
12 | indent_style = space
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.env.template:
--------------------------------------------------------------------------------
1 | PUBLIC_URL=http://localhost:3000/playground
2 | REACT_APP_API_ENDPOINT=http://localhost:8181/api
3 | REACT_APP_API_TOKEN=auth-token
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | tailwind.config.js
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | package.json
3 | public/
4 | *.css
5 | tsconfig.json
6 | tailwind.config.js
7 | *.md
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## talos
2 |
3 | Talos is meant to be a powerful interface to easily create automated workflows
4 | that uses large language models (LLMs) to parse, extract, summarize, etc. different
5 | types of content and push it to other APIs/databases/etc.
6 |
7 | Think [Zapier](https://en.wikipedia.org/wiki/Zapier) but with language model magic 🪄.
8 |
9 |
10 |
11 |
12 |
13 |
14 | Click to play video. Note: the summarization step is sped up (~1 minute).
15 |
16 | ## Example Workflows
17 |
18 | ### Read this Wikipedia page & extract some data from it.
19 |
20 | [View workflow](./docs/examples/webpage-extract.png)
21 |
22 | 1. Fetch this wikipedia page.
23 | 2. Extract who the page is about, give a summary, and extract any topics discussed.
24 | 3. Put into a template.
25 |
26 | ### Read a Yelp review & tell me about it.
27 |
28 | 1. Read this Yelp review
29 | 2. Extract the sentiment and give me a list of complaints and/or praises
30 | 3. Put into a report template.
31 | 4. Email it to me.
32 |
33 | ### Summarize this book & generate a book report.
34 |
35 | 1. Read through this PDF file.
36 | 2. Create a bullet point summary of the entire book.
37 | 3. Generate key takeaways from the book
38 | 3a. For each takeaway, elaborate
39 | 4. Combine into a template.
40 |
41 | ## Running Locally
42 |
43 | To start running `talos` locally, install the dependencies
44 |
45 | ```bash
46 | # Copy the environment variables
47 | > cp .env.template .env.local
48 | # Install dependencies
49 | > npm install
50 | # Start the front-end
51 | > npm run start
52 | ```
53 |
54 | The UI will be available at [http://localhost:3000/playground](http://localhost:3000/playground).
55 |
56 | Now we need to start the backend.
57 |
58 | ### Using w/ memex
59 |
60 | [memex](https://github.com/spyglass-search/memex) is a self-hosted LLM backend &
61 | memory store that exposes basic LLM functionality as a RESTful API.
62 |
63 | You will need to either download a local LLM model to use or add your OpenAI to
64 | the `.env.template` file to get started.
65 |
66 | ```bash
67 | > git clone https://github.com/spyglass-search/memex
68 | > cd memex
69 | > docker-compose up
70 | ```
71 |
--------------------------------------------------------------------------------
/docs/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spyglass-search/talos/2e691abf83e0e205baf951914c08c20fe72389c5/docs/example.gif
--------------------------------------------------------------------------------
/docs/examples/webpage-extract.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spyglass-search/talos/2e691abf83e0e205baf951914c08c20fe72389c5/docs/examples/webpage-extract.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@handlebars/parser": "^2.1.0",
7 | "@heroicons/react": "^2.0.18",
8 | "@icons-pack/react-simple-icons": "^9.1.0",
9 | "@tailwindcss/forms": "^0.5.4",
10 | "@tailwindcss/typography": "^0.5.9",
11 | "@testing-library/jest-dom": "^5.17.0",
12 | "@testing-library/react": "^13.4.0",
13 | "@testing-library/user-event": "^13.5.0",
14 | "ajv": "^8.12.0",
15 | "axios": "^1.4.0",
16 | "daisyui": "^3.5.1",
17 | "handlebars": "^4.7.8",
18 | "luxon": "^3.4.0",
19 | "mixpanel-browser": "^2.47.0",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-scripts": "5.0.1",
23 | "rxjs": "^7.8.1",
24 | "typescript": "^4.9.5",
25 | "url": "^0.11.1",
26 | "web-vitals": "^2.1.4"
27 | },
28 | "scripts": {
29 | "start": "react-scripts start",
30 | "build": "react-scripts build",
31 | "test": "react-scripts test",
32 | "eject": "react-scripts eject",
33 | "lint": "eslint .",
34 | "fmt": "prettier --write ."
35 | },
36 | "eslintConfig": {
37 | "extends": [
38 | "react-app",
39 | "react-app/jest"
40 | ]
41 | },
42 | "browserslist": {
43 | "production": [
44 | ">0.2%",
45 | "not dead",
46 | "not op_mini all"
47 | ],
48 | "development": [
49 | "last 1 chrome version",
50 | "last 1 firefox version",
51 | "last 1 safari version"
52 | ]
53 | },
54 | "devDependencies": {
55 | "@types/ajv": "^1.0.0",
56 | "@types/jest": "^27.5.2",
57 | "@types/json-schema": "^7.0.12",
58 | "@types/luxon": "^3.3.1",
59 | "@types/mixpanel-browser": "^2.47.1",
60 | "@types/node": "^16.18.40",
61 | "@types/react": "^18.2.19",
62 | "@types/react-dom": "^18.2.7",
63 | "prettier": "3.0.1",
64 | "tailwindcss": "^3.3.3"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spyglass-search/talos/2e691abf83e0e205baf951914c08c20fe72389c5/public/favicon.png
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
21 |
22 |
31 | talos: AI Workflow Builder
32 |
37 |
38 |
39 |
40 |
41 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/public/logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spyglass-search/talos/2e691abf83e0e205baf951914c08c20fe72389c5/public/logo@2x.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/workflow-examples/initial.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "uuid": "f55f17e4-61bd-40b4-8ea9-f07103193ea5",
4 | "label": "Initial Data",
5 | "nodeType": "DataSource",
6 | "data": {
7 | "type": "Text",
8 | "content": "Fireball\nLvl 3 Evocation\n\nCasting Time: 1 action\nRange: 150 feet\nTarget: A point you choose within range\nComponents: V S M (A tiny ball of bat guano and sulfur)\nDuration: Instantaneous\nClasses: Sorcerer, Wizard\nA bright streak flashes from your pointing finger to a point you choose within range and then blossoms with a low roar into an explosion of flame. Each creature in a 20-foot-radius sphere centered on that point must make a Dexterity saving throw. A target takes 8d6 fire damage on a failed save, or half as much damage on a successful one. The fire spreads around corners. It ignites flammable objects in the area that aren’t being worn or carried.\nAt Higher Levels: When you cast this spell using a spell slot of 4th level or higher, the damage increases by 1d6 for each slot level above 3rd."
9 | }
10 | },
11 | {
12 | "uuid": "5c1ea745-b9e4-44c3-a059-6a83f3bada6a",
13 | "label": "Extract Spell Data",
14 | "nodeType": "Extract",
15 | "data": {
16 | "query": "extract the level, name, casting time, classes, range, and damage of this spell",
17 | "schema": {
18 | "$schema": "https://json-schema.org/draft/2020-12/schema",
19 | "type": "object",
20 | "properties": {
21 | "level": {
22 | "type": "number"
23 | },
24 | "name": {
25 | "type": "string"
26 | },
27 | "castingTime": {
28 | "type": "string"
29 | },
30 | "classes": {
31 | "type": "array",
32 | "items": {
33 | "type": "string"
34 | }
35 | },
36 | "range": {
37 | "type": "string"
38 | },
39 | "damage": {
40 | "type": "string"
41 | }
42 | },
43 | "required": [
44 | "name",
45 | "castingTime",
46 | "classes"
47 | ]
48 | }
49 | }
50 | },
51 | {
52 | "uuid": "template12413",
53 | "label": "Generate Template",
54 | "nodeType": "Template",
55 | "data": {
56 | "template": "{{name}} (Lvl. {{level}}) takes {{action}} and does {{damage}}.\n\nClasses:\n{{#each classes}}\n- {{.}}\n{{/each}}",
57 | "varMapping": {
58 | "name": "name",
59 | "level": "level",
60 | "classes": "classes",
61 | "action": "castingTime",
62 | "damage": "damage"
63 | }
64 | }
65 | }
66 | ]
67 |
--------------------------------------------------------------------------------
/public/workflow-examples/sentiment-analysis.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "uuid": "50bffddf-1c43-4d82-b6e8-60633ac4e173",
4 | "label": "Example Yelp Review",
5 | "nodeType": "DataSource",
6 | "data": {
7 | "content": "So disappointed.\nthe food really. I got the pork belly \"carbonara\" and not only was the pork belly the worst I've ever had (a was cold and undercooked), but the rice cakes were even . And maybe it was the yolk that made the rice cakes taste bad but something was off and literally had to gulp down my food with the wine. I was so disappointed; I haven't had such a bad man in such a long time. I had a decision to make at that time, say something or not. And despite being a $28 meal that I absolutely hated, I chickened out and paid the full amount. I know probably the wrong decision but I just couldn't bring myself to have them take it back because I'd feel terrible. I hate doing that. So probably the wrong decision but here we are.\n\nOn the other hand, the service was absolutely fantastic! Everyone was so nice. I greatly appreciated that!\nTo drink I had the cab. Tasty and lighter than most cabs.\n\nI'd probably come back for the wine/drinks. The food? I don't think so",
8 | "type": "Text"
9 | }
10 | },
11 | {
12 | "uuid": "4a4e7377-4a1f-40f0-bf34-5e1be953d4c4",
13 | "label": "Extract node",
14 | "nodeType": "Extract",
15 | "data": {
16 | "query": "extract the sentiment and complaints from this review",
17 | "schema": {
18 | "$schema": "https://json-schema.org/draft/2020-12/schema",
19 | "type": "object",
20 | "properties": {
21 | "sentiment": {
22 | "type": "string",
23 | "enum": [
24 | "happy",
25 | "satisfied",
26 | "dissatisfied",
27 | "angry"
28 | ]
29 | },
30 | "complaints": {
31 | "type": "array",
32 | "items": {
33 | "type": "string"
34 | }
35 | }
36 | },
37 | "required": [
38 | "complaints",
39 | "sentiment"
40 | ]
41 | }
42 | }
43 | },
44 | {
45 | "uuid": "0eebb7a3-f2d0-4c47-b394-93d070d21ba5",
46 | "label": "Template node",
47 | "nodeType": "Template",
48 | "data": {
49 | "template": "Review Report\n\nThe customer was: {{sentiment}}\n\nComplaints:\n{{#each complaints}}\n- {{.}}\n{{/each}}",
50 | "varMapping": {
51 | "sentiment": "sentiment",
52 | "complaints": "complaints"
53 | }
54 | }
55 | }
56 | ]
57 |
--------------------------------------------------------------------------------
/public/workflow-examples/webpage-parsing.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "uuid": "55f2859f-8fe6-4d5e-b060-5a9255157ed9",
4 | "label": "Fetch Webpage",
5 | "nodeType": "DataSource",
6 | "data": {
7 | "url": "https://en.wikipedia.org/wiki/Katalin_Karik%C3%B3",
8 | "content": "Fireball\nLvl 3 Evocation\n\nCasting Time: 1 action\nRange: 150 feet\nTarget: A point you choose within range\nComponents: V S M (A tiny ball of bat guano and sulfur)\nDuration: Instantaneous\nClasses: Sorcerer, Wizard\nA bright streak flashes from your pointing finger to a point you choose within range and then blossoms with a low roar into an explosion of flame. Each creature in a 20-foot-radius sphere centered on that point must make a Dexterity saving throw. A target takes 8d6 fire damage on a failed save, or half as much damage on a successful one. The fire spreads around corners. It ignites flammable objects in the area that aren’t being worn or carried.\nAt Higher Levels: When you cast this spell using a spell slot of 4th level or higher, the damage increases by 1d6 for each slot level above 3rd.",
9 | "type": "Url"
10 | }
11 | },
12 | {
13 | "uuid": "ae63ce1a-80f6-4660-8998-913db769d58c",
14 | "label": "Parse Page",
15 | "nodeType": "Extract",
16 | "data": {
17 | "query": "extract the page name and any topics talked about ont he page and create a summary",
18 | "schema": {
19 | "$schema": "https://json-schema.org/draft/2020-12/schema",
20 | "type": "object",
21 | "properties": {
22 | "pageTitle": {
23 | "type": "string"
24 | },
25 | "summary": {
26 | "type": "string"
27 | },
28 | "topics": {
29 | "type": "array",
30 | "items": {
31 | "type": "string"
32 | }
33 | }
34 | },
35 | "required": [
36 | "pageTitle",
37 | "summary",
38 | "topics"
39 | ]
40 | }
41 | }
42 | },
43 | {
44 | "uuid": "f4fc8611-0207-4fed-949d-e41a21e37727",
45 | "label": "Template node",
46 | "nodeType": "Template",
47 | "data": {
48 | "template": "This page is about: {{ pageTitle}}\n\nHere is a summary:\n{{ summary }}\n\nAnd the topics that are discussed\n{{#each topics}}\n- {{.}}\n{{/each}}",
49 | "varMapping": {
50 | "pageTitle": "pageTitle",
51 | "summary": "summary",
52 | "topics": "topics"
53 | }
54 | }
55 | }
56 | ]
57 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from "react";
2 | import axios from "axios";
3 | import {
4 | ArrowDownIcon,
5 | ArrowUpCircleIcon,
6 | DocumentArrowDownIcon,
7 | PlayIcon,
8 | PlusCircleIcon,
9 | TrashIcon,
10 | } from "@heroicons/react/20/solid";
11 | import {
12 | NodeDef,
13 | NodeResult,
14 | LastRunDetails,
15 | NodeUpdates,
16 | NodeType,
17 | DataNodeType,
18 | ParentDataDef,
19 | OutputDataType,
20 | DataNodeDef,
21 | NodeResultStatus,
22 | NodeState,
23 | } from "./types/node";
24 | import { NodeComponent, WorkflowResult } from "./components/nodes";
25 | import { useState } from "react";
26 | import {
27 | getPreviousUuid,
28 | insertNode,
29 | loadWorkflow,
30 | nodeComesAfter,
31 | removeNode,
32 | saveWorkflow,
33 | } from "./workflows/utils";
34 | import { cancelExecution } from "./workflows/executor";
35 | import { ModalType } from "./types";
36 | import AddNodeModal from "./components/modal/AddNodeModal";
37 | import { runWorkflow } from "./workflows";
38 | import { createNodeDefFromType } from "./utils/nodeUtils";
39 | import { ConfigureMappingModal } from "./components/modal/ConfigureMappingModal";
40 | import { API_TOKEN } from "./workflows/task-executor";
41 | import {
42 | InputOutputDefinition,
43 | canConfigureMappings,
44 | generateInputOutputTypes,
45 | } from "./types/typeutils";
46 | import { DropArea } from "./components/nodes/dropArea";
47 | import { NodeDivider } from "./components/nodes/nodeDivider";
48 | import {
49 | ValidationStatus,
50 | WorkflowValidationResult,
51 | validateWorkflow,
52 | } from "./utils/workflowValidator";
53 |
54 | function AddAction({ onAdd = () => {} }: { onAdd: () => void }) {
55 | return (
56 |
57 |
61 |
62 | );
63 | }
64 |
65 | function App() {
66 | // Is a workflow currently running?
67 | let [workflow, setWorkflow] = useState>([]);
68 | let [workflowDataTypes, setWorkflowDataTypes] = useState<
69 | InputOutputDefinition[]
70 | >([]);
71 | let [validationResult, setValidationResult] = useState<
72 | WorkflowValidationResult | undefined
73 | >(undefined);
74 | let [nodeResults, setNodeResults] = useState